12. Sympy: un sistema de álgebra computacional

SymPy es un sistema de álgebra computacional (CAS) escrito en Python puro, está desarrollado con un enfoque en la extensibilidad y facilidad de uso, tanto a través de aplicaciones interactivas como programáticas. Estas características le han permitido a SymPy convertirse en una librería de cómputo simbólico muy popular en el ecosistema científico de Python.

En todo este capítulo se asumirá que se ha importado la librería SymPy, con las siguientes instrucciones:

from sympy import *

Adicionalmente dentro del entorno de Jupyter podemos ejecutar la siguiente instrucción que permite renderizar las expresiones resultantes.

init_printing()

12.1. Variables simbólicas

Las variables simbólicas son el alma de SymPy, todas las operaciones de álgebra simbólica se basan en estas. A una variable simbólica se le asigna un símbolo que la representa mediante la función symbols:

x = symbols("x")
print(type(x))
<class 'sympy.core.symbol.Symbol'>

Con la función symbols se define una nueva variable simbólica que se guarda en x, se verifica que se crea un objeto de la clase Symbol. Con la variable x ya definida se puede comenzar a formar expresiones algebraicas y manipular matemáticamente.

x + 2
_images/SymPy_7_0.png
x**2 - 2*x - 10
_images/SymPy_8_0.png
sqrt(x) - sin(x)
_images/SymPy_9_0.png

Puede definir múltiples variables separando por comas cada representación simbólica:

p,q,r = symbols("p,q,r")
p**q + r/q
_images/SymPy_11_0.png

Además de la forma anterior, también es posible tener variables simbólicas disponibles si se importan del módulo abc de SymPy:

from sympy.abc import a,b,c,d
(a + b)*(c + d)
_images/SymPy_14_0.png

12.2. Manipulación algebraica

SymPy es una poderosa herramienta de manipulación y simplificación algebraica, en lo subsiguiente se revisarán algunos casos elementales y se describirá el uso de las herramientas (funciones) que proporciona.

En primera instancia se definen algunas variables simbólicas a utilizar:

x,y,z = symbols("x,y,z")
a,b,c,d,k,m,n = symbols("a,b,c,d,k,m,n")

Para las expresiones algebraicas formadas en SymPy por default se evalúan y simplifican los términos semejantes. Vea los siguientes casos:

x**2 + 5*x**3 - 10*x**2 + 5*x - 10*(x + 1)
_images/SymPy_18_0.png
a*b + c*d + x/y - 7*a*b
_images/SymPy_19_0.png

Naturalmente para simplificaciones y operaciones un poco menos obvias, habrá que especificarle lo que se requiere.

Una de las operaciones elementales de simplificación en álgebra es la factorización, SymPy dispone de la función factor, la cual toma una expresión algebraica y la factoriza conforme sea posible. Por ejemplo, suponiendo que se tiene la expresión \(ab + ac\), es sencillo identificar que se puede
factorizar como \(a(b+c)\), SymPy hace lo mismo sin sobresalto alguno.

factor(a*b + a*c)
_images/SymPy_21_0.png

De igual forma se sabe que una expresión de la forma \( ac + ad + bc + bd \) se puede factorizar como \( (a + b) (d + c) \).

factor( a*c + a*d + b*c + b*d )
_images/SymPy_23_0.png

Así, para un binomio al cuadrado se sabe que, \( (a+b)(a+b) \) es la factorización de una expresión del tipo \( a^2 + 2ab + b^2 \).

factor(a**2 + 2*a*b + b**2)
_images/SymPy_25_0.png

¿Se puede hacer el proceso inverso, es decir, dados dos factores obtener su expresión expandida? Por supuesto, para ello se hace uso de la función expand.

expand( (x-7)*(x-3) )
_images/SymPy_27_0.png

SymPy puede manejar cualquier cantidad de factores y devolver la expresión que resulta de realizar la multiplicación algebraica, sin estar limitada siquiera por la cantidad de variables simbólicas.

expand( (x + y)*(x - y) )
_images/SymPy_29_0.png
expand( (x + y)*(x+y) )
_images/SymPy_30_0.png
expand( (x + y)**2 )
_images/SymPy_31_0.png
expand( (x + y)**3 )
_images/SymPy_32_0.png
expand( a*(m + n)**2 )
_images/SymPy_33_0.png

De manera general, si requiere simplificar una expresión algebraica SymPy dispone de una función más o menos universal que funcionará en la mayoría de los casos: simplify. Por ejemplo, suponiendo que se tiene la siguiente expresión algebraica racional y se requiere reducir lo más posible:

\[ \frac{x^2 - 3x}{x^2 + 3x}\]

Se puede notar que tanto en el numerador como en el denominador se puede factorizar \(x\), lo cual conduce a:

\[ \frac{x(x - 3)}{x(x + 3)} = \frac{x-3}{x+3} \]

Con SymPy:

simplify( (x**2 - 3*x)/(x**2 + 3*x) )
_images/SymPy_35_0.png

Lo mismo para una expresión que involucre funciones trigonométricas, por ejemplo, cualquiera que haya cursado matemáticas del nivel secundario, al menos, sabe que \(\sin^2 x + \cos^2 x = 1 \), y también lo sabe SymPy:

simplify( sin(x)**2 + cos(x)**2 )
_images/SymPy_37_0.png

Pero también sabe que \( \cos x \cos y - \sin x \sin y = \cos(x+y) \).

simplify( cos(x)*cos(y) - sin(x)*sin(y) )
_images/SymPy_39_0.png

O bien:

simplify( (1+tan(x)**2)/(1+cot(x)**2) )
_images/SymPy_41_0.png

Habitualmente para la manipulación de expresiones que contienen funciones trigonométricas se suele utilizar la función trigsimp que en muchos casos lo que devuelva coincidirá con simplify. Sin embargo, si en lugar de reducir una expresión trigonométrica se requiere expandirla, como en el caso del coseno o seno de la suma de dos ángulos, probablemente por intuición se utilizaría expand, pero aquí no funciona como puede verificar.

expand( sin(x + y) )
_images/SymPy_43_0.png

En este caso puede utilizar la función expand_trig, la cual maneja de mejor manera las manipulaciones trigonométricas:

expand_trig( sin(x+y) )
_images/SymPy_45_0.png

12.3. Resolviendo ecuaciones e inecuaciones

SymPy dispone de la función solve, la cual resuelve desde ecuaciones polinomiales, sistemas de ecuaciones lineales, inecuaciones, hasta sistemas de ecuaciones no lineales. La sintaxis de solve es polimórfica y en general depende de lo que se requiera resolver, tendiendo siempre a que sea posible especificar el mínimo número de parámetros.

En las siguientes subsecciones se describen algunos casos, se asumirá en lo subsiguiente que además de importar SymPy se definieron las siguientes variables simbólicas:

x,y,z = symbols("x,y,z")
a,b,c = symbols("a,b,c")

12.3.1. Ecuaciones polinómicas

La sintaxis más elemental de solve es pasando un sólo argumento, el cual se espera sea una expresión algebraica y se considera que esta estará igualada a cero. Por ejemplo para resolver la ecuación lineal \( x - 3 = 0 \):

solve( x - 3 )
_images/SymPy_50_0.png

Para una ecuación cuadrática \( x^2 + 2x + 2 = 0 \):

solve(x**2 + 2*x + 2)
_images/SymPy_52_0.png

En este caso la ecuación tiene soluciones complejas, la unidad imaginaria en SymPy se especifica mediante el símbolo I.

Si se quisiera resolver la ecuación cuadrática en su forma general, por intuición y lo que sabemos hasta ahora, haríamos:

solve(a*x**2 + b*x + c)
_images/SymPy_54_0.png

Note que el problema está en que al haber más de una variable simbólica, SymPy no sabe con respecto a qué variable debe resolver y toma la primera en orden alfabético. Para especificar explícitamente respecto a qué variable resolver, se puede indicar mediante un segundo argumento:

solve(a*x**2 + b*x + c, x)
_images/SymPy_56_0.png

Puede verificar que devuelve, en efecto, la tan conocida fórmula general.

12.3.2. Ecuaciones trigonométricas

12.4. Cálculo

12.4.1. Límites

Para calcular límites matemáticos SymPy dispone de la función limit, la cual requiere al menos tres argumentos de entrada: la función, variable y valor al que tiende. Por ejemplo, si se quiere calcular el siguiente límite:

\[ \lim_{x \to 0} \frac{\sin x}{x} \]

Tendría que teclearse lo siguiente:

limit(sin(x)/x, x, 0)
_images/SymPy_60_0.png

Para calcular limites laterales debe pasarse un cuarto argumento, por ejemplo:

limit(1/(x-5), x, 5, "-")
_images/SymPy_62_0.png
limit(1/(x-5), x, 5, "+")
_images/SymPy_63_0.png

El símbolo + denota el cálculo de un límite lateral por la derecha y el símbolo - un límite lateral por la izquierda.

12.4.2. Derivadas

Las derivadas en Python se calculan utilizando la función diff, misma que en su forma más simple espera al menos dos argumentos: una expresión algebraica y una variable respecto a la cual derivar. Por ejemplo:

diff(exp(x)*cos(x), x)
_images/SymPy_66_0.png

Es posible también especificar una derivada de orden superior mediante un tercer argumento:

diff(5*x**2 + 3*x - 10, x, 2)
_images/SymPy_68_0.png

La instrucción anterior calcula la segunda derivada de la función \( f(x) = 5x^2 + 3x - 10 \).

12.4.3. Integrales

Para calcular integrales vamos a utilizar la función integrate, la cual acepta al menos dos argumentos: la función a integrar y la expresión respecto a la cual se integra, por ejemplo:

integrate(x**2 + 3*x - 7, x)
_images/SymPy_71_0.png

Esa instrucción calcula la integral:

\[ \int \left( x^2 + 3x - 7 \, \right) \,\, dx = \frac{x^3}{3} + \frac{3x^2}{2} - 7x + C \]

Note que la expresión algebraica devuelta por Python no contiene la constante de integración, por default SymPy no la considera. Sí en algún caso específico necesita referir a la constante de integración puede adicionarla manualmente.

Las integrales definidas se pueden calcular si el segundo argumento se hace una tupla de la forma (variable, a, b), donde a y b indican el límite inferior y superior a evaluar en la integral:

Por ejemplo, para evaluar:

\[ \int_0^{\pi} \sin x \, dx \]
integrate(sin(x), (x, 0, pi))
_images/SymPy_73_0.png

O para:

\[ \int_{-5}^{5} z^2 \, dz \]
integrate(z**2, (z,-5,5))
_images/SymPy_75_0.png

12.5. Vectores

Un vector denota una cantidad física que tiene magnitud y orientación en un determinado sistema de referencia. En SymPy los vectores se pueden definir mediante la clase Matrix del módulo matrices, de tal manera que en primera instancia haría falta importar dicho módulo, para esto hacemos:

from sympy.matrices import Matrix

Ahora vamos a definir dos vectores \(\vec{u}\) y \(\vec{v}\):

\[\begin{split} \vec{u} = \begin{bmatrix} 2 \\ 1 \\ -5 \end{bmatrix} \qquad ; \qquad \vec{v} = \begin{bmatrix} 4 \\ -1 \\ 3 \end{bmatrix} \end{split}\]
u = Matrix([2,1,-5])
v = Matrix([4,-1,3])

Una suma y resta vectorial se pueden ejecutar sin muchas complicaciones, mediante los operadores aritméticos ya conocidos.

u + v
_images/SymPy_82_0.png
v - u
_images/SymPy_83_0.png

La magnitud de un vector se puede calcular utilizando el método norm.

u.norm()
_images/SymPy_85_0.png
v.norm()
_images/SymPy_86_0.png

Recuerde que si requiere ver las expresiones resultantes como fracciones decimales debe usar evalf.

El producto escalar de dos vectores puede calcularlo utilizando el método dot, por ejemplo \( \vec{u} \cdot \vec{v} \) lo puede especificar como:

u.dot(v)
_images/SymPy_88_0.png

Para calcular el producto vectorial utilice el método cross, por ejemplo \( \vec{u} \times \vec{v} \):

u.cross(v)
_images/SymPy_90_0.png

Recuerde que el producto vectorial no es conmutativo, por tanto, \(\vec{v} \times \vec{u} \) resultará en un vector diferente al obtenido anteriormente, como puede verificar enseguida:

v.cross(u)
_images/SymPy_92_0.png

12.6. Matrices

Las matrices son arreglos rectangulares de números o cantidades simbólicas. En SymPy, se definen utilizando la clase Matrix, pasándole como argumentos una lista de listas, donde cada sublista corresponde a una fila de la matriz.

Por ejemplo, vamos a definir las matrices \(A\), \(B\) y \(C\), dadas por:

\[\begin{split} A = \begin{bmatrix} 20 & 50 & 100 \\ 10 & 35 & 200 \\ -30 & 20 & 80 \end{bmatrix} \qquad B = \begin{bmatrix} 12 & 26 & 45 \\ 3 & -15 & 18 \\ 54 & 20 & 0 \end{bmatrix} \qquad C = \begin{bmatrix} 5 & 9 \\ 2 & 3 \\ -10 & 8 \end{bmatrix} \end{split}\]

Primero, importamos la clase Matrix del módulo matrices:

from sympy.matrices import Matrix

Luego escribiríamos:

A = Matrix([[20,50,100], [10,35,200], [-30,20,80]])
B = Matrix([[12,26,45],[3,-15,18],[54,20,0]])
C = Matrix([[5,9], [2,3], [-10,8]])

De manera muy sencilla podríamos realizar operaciones matriciales básicas, por ejemplo, una suma:

A + B
_images/SymPy_98_0.png

Naturalmente, y como es esperable, SymPy conoce y aplica las reglas de álgebra de matrices, observe:

A + C
---------------------------------------------------------------------------
ShapeError                                Traceback (most recent call last)
<ipython-input-57-c3cb66996588> in <module>()
----> 1 A + C

~\Anaconda3\lib\site-packages\sympy\core\decorators.py in binary_op_wrapper(self, other)
    130                     else:
    131                         return f(self)
--> 132             return func(self, other)
    133         return binary_op_wrapper
    134     return priority_decorator

~\Anaconda3\lib\site-packages\sympy\matrices\common.py in __add__(self, other)
   1949             if self.shape != other.shape:
   1950                 raise ShapeError("Matrix size mismatch: %s + %s" % (
-> 1951                     self.shape, other.shape))
   1952 
   1953         # honest sympy matrices defer to their class's routine

ShapeError: Matrix size mismatch: (3, 3) + (3, 2)

SymPy es muy explícito en ese tipo de situaciones y nos imprime un mensaje de error suficientemente descriptivo. De igual forma que es válido realizar el producto \(AC\), pero no \(CA\):

A*C
_images/SymPy_102_0.png
C*A
---------------------------------------------------------------------------
ShapeError                                Traceback (most recent call last)
<ipython-input-59-cbae4c009e52> in <module>()
----> 1 C*A

~\Anaconda3\lib\site-packages\sympy\core\decorators.py in binary_op_wrapper(self, other)
    130                     else:
    131                         return f(self)
--> 132             return func(self, other)
    133         return binary_op_wrapper
    134     return priority_decorator

~\Anaconda3\lib\site-packages\sympy\matrices\common.py in __mul__(self, other)
   2006             if self.shape[1] != other.shape[0]:
   2007                 raise ShapeError("Matrix size mismatch: %s * %s." % (
-> 2008                     self.shape, other.shape))
   2009 
   2010         # honest sympy matrices defer to their class's routine

ShapeError: Matrix size mismatch: (3, 2) * (3, 3).

El determinante de una matriz podemos calcularlo mediante la función det:

det(B)
_images/SymPy_105_0.png

O bien, mediante el propio método det:

B.det()
_images/SymPy_107_0.png

La matriz inversa podemos calcularla utilizando el método inv:

A.inv()
_images/SymPy_109_0.png
A.inv()*A
_images/SymPy_110_0.png

La transpuesta de una matriz se puede obtener accediendo al atributo T

C.T
_images/SymPy_112_0.png

12.7. Ejercicios

Utilice Python/SymPy para resolver los siguientes ejercicios:

  1. Resuelva las siguientes ecuaciones (para la variable \(x\)):

  2. \( x + 3x = 10 \)

  3. \( 2x^2 - 10x + 5 = 0 \)

  4. \( \frac{a}{x} + bx - 8 = 0 \)

  5. \( \cos x + \sin x = 10 \)

  6. \( kx - 10 = x^2 \)

  7. Calcule las siguientes derivadas:

  8. \( \frac{d}{dx} \left( \cos x \right) \)

  9. \( \frac{d}{dt} \left( at^2 - 2\tan t - \frac{10}{t} \right) \)

  10. \( \frac{d}{dz} \left( z^5 - 10 e^{-z} \right) \)

  11. Calcule las siguientes integrales indefinidas:

  12. \( \int \cos x \, dx \)

  13. \( \int \left( x^3 - 8x \right)\, dx \)

  14. \( \int \left( e^{-3x} \sin x \right) \, dx \)

  15. \( \int \left( s^2 + ks - m \right) \, ds \)

  16. Calcule las siguientes integrales definidas:

  17. \( \int_{a}^{b} \sin x \, dx \)

  18. \( \int_{-10}^{5} x^2 + 10x \, dx \)

  19. \( \int_{0}^{2} 10e^{-z} \, dz \)

  20. Resuelva la siguiente ecuación diferencial: $\( \frac{dS}{dr} = kS \)$

  21. Escriba una función que dados como entrada dos vectores, determine sí estos son paralelos.

  22. Desarrolle una función llamada calcula_fuerza_resultante que reciba como datos de entrada un conjunto de vectores fuerza y devuelva el vector de fuerza resultante.

  23. Escriba un función llamada calcula_momento_resultante que reciba como datos de entrada un conjunto de vectores fuerza y un conjunto de vectores de posición del punto de aplicación de la fuerza con respecto a un cierto punto. Se deberá devolver el momento total producido por las fuerzas con respecto al punto.