Computación simbólica con Sympy#

Important

Para tener presente

El paquete sympy nos brinda la posibilidad de usar expresiones aproximadas de manera simbólica privilegiando el símbolo frente a la evaluación.

Computación simbólica#

Hasta el momento hemos trabajado con varios elementos matemáticos que en ocasiones se toman de manera aproximada, por ejemplo, la raíz cuadrada de un número entero. Aunque hay algunos números cuya raíz es entera la mayoría tiene raices cuadradas irracionales y la máquina nos ofrece apenas una aproximación a esos valores.

import numpy as np
np.sqrt(8)
2.8284271247461903
np.sqrt(8)*np.sqrt(8)
8.000000000000002
import sympy
sympy.sqrt(8)
---------------------------------------------------------------------------
ModuleNotFoundError                       Traceback (most recent call last)
Input In [3], in <cell line: 1>()
----> 1 import sympy
      2 sympy.sqrt(8)

ModuleNotFoundError: No module named 'sympy'
sympy.sqrt(8)*sympy.sqrt(8)
\[\displaystyle 8\]
Nota: para obtener una salida estética de sympy usamos la orden:

from sympy import init_printing

init_printing() 

El poder de la computación simbólica

El poder real de un sistema de cálculo simbólico como SymPy es la capacidad de hacer todo tipo de cálculos simbólicamente. SymPy puede simplificar expresiones, calcular derivadas, integrales y límites, resolver ecuaciones, trabajar con matrices y mucho, mucho más, y hacerlo todo simbólicamente. Incluye módulos para trazar, imprimir (como salida impresa en 2D de fórmulas matemáticas, LATEX), generación de código, física, estadística, combinatoria, teoría de números, geometría, lógica y más.

Tomado de https://docs.sympy.org/latest/tutorial/intro.html

Conceptos básicos#

Para iniciar el trabajo con la librería sympy debemos aclarar rutinas que son fundamentales para iniciar cualquier trabajo con este paquete. En primer lugar, por ejemplo, hay que aclarar que vamos a usar un símbolo así mismo, las ecuaciones tienen que poder expresarse y no podemos usar ni = ni ==. Veamos algunos de estos conceptos básicos.

Símbolos#

Para usar variables simbólicas debemos definirlas, usamos la función symbols:

import sympy as sp
x = sp.symbols("x")
x
\[\displaystyle x\]

Notemos que las operaciones las realiza de manera análoga a lo que solemos hacer con lápiz y papel:

(x+1)**2
\[\displaystyle \left(x + 1\right)^{2}\]
sp.expand((x+1)**2)
\[\displaystyle x^{2} + 2 x + 1\]
(3*x-5)*(2*x+7)-x
\[\displaystyle - x + \left(2 x + 7\right) \left(3 x - 5\right)\]
x+2*x-8+5*x+7
\[\displaystyle 8 x - 1\]
comoescribo = sp.symbols("comoveo")
comoescribo+8 
\[\displaystyle comoveo + 8\]

Sustitución#

Para remplazar valores en una expresión usamos subs

expr = (3*x-5)*(2*x+7)-x
expr
\[\displaystyle - x + \left(2 x + 7\right) \left(3 x - 5\right)\]
expr.subs(x,1)
\[\displaystyle -19\]
expr.subs(x,0)
\[\displaystyle -35\]

También se pueden sustituir expresiones

y=sp.symbols("y")
expr = x**y
expr
\[\displaystyle x^{y}\]
expr = expr.subs(y, x**y)
expr
\[\displaystyle x^{x^{y}}\]
from sympy import *
expr = sin(2*x)
expand_trig(expr)
\[\displaystyle 2 \sin{\left(x \right)} \cos{\left(x \right)}\]
expr.subs(sin(2*x), 2*sin(x)*cos(x)).subs(cos(2*x),2*cos(x)**2-1)
\[\displaystyle 2 \sin{\left(x \right)} \cos{\left(x \right)}\]
z = symbols("z")
expr = x**3 + 4*x*y - z
expr
\[\displaystyle x^{3} + 4 x y - z\]
expr.subs([(x, 2), (y, 4), (z, 0)])
\[\displaystyle 40\]

Convertir cadenas a expresiones SymPy#

Usamos sympify para convertir cadenas en expresiones:

str_expr = "x**2 + 3*x - 1/2"
print(str_expr)
expr = sympify(str_expr)
expr
x**2 + 3*x - 1/2
\[\displaystyle x^{2} + 3 x - \frac{1}{2}\]
import ipywidgets as widgets
from ipywidgets import interact
def modulo1(t,n):
    z="Error"
    n=sympify(n)
    try:
        t=sympify(t)
        z=sp.expand(t**n)
    except:
        print('Error!!')
    return z
widn=widgets.IntSlider(min=0,max=10,value=1)
interact(modulo1, t="sin(x)",n=widn)
<function __main__.modulo1(t, n)>

Obtener el valor en punto flotante#

Usamos evalf para obtener el valor en punto flotante.

expr = sqrt(8)
expr
\[\displaystyle 2 \sqrt{2}\]
expr.evalf()
\[\displaystyle 2.82842712474619\]

subs y evalf son buenos si desea hacer una evaluación simple, pero si tiene la intención de evaluar una expresión en muchos puntos, hay formas más eficientes. Por ejemplo, si desea evaluar una expresión en mil puntos, usar SymPy sería mucho más lento de lo necesario, especialmente si solo le importa la precisión de la máquina. En su lugar, debe usar librerías como NumPy y SciPy .

La forma más fácil de convertir una expresión SymPy en una expresión que puede evaluarse numéricamente es usar la lambdifyfunción. lambdify actúa como una función lambda, excepto que convierte los nombres de SymPy a los nombres de la biblioteca numérica dada, generalmente NumPy. Por ejemplo:

import numpy as np
a = np.arange(10) 
a
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
expr = sin(x)
f = lambdify(x, expr, "numpy") 
f(a) 
array([ 0.        ,  0.84147098,  0.90929743,  0.14112001, -0.7568025 ,
       -0.95892427, -0.2794155 ,  0.6569866 ,  0.98935825,  0.41211849])
f = lambdify(x, expr, "math")
f(5)
-0.9589242746631385

Ecuaciones#

Como sabemos = y == ya fueron usado en el lenguaje para unas operaciones específicas.

x+1==4
False
sp.Eq(x + 1, 4)
\[\displaystyle x + 1 = 4\]

Igualdad de expresiones y simplificación#

Ahora saltemos y hagamos algunas matemáticas interesantes. Una de las características más útiles de un sistema de manipulación simbólica es la capacidad de simplificar expresiones matemáticas.

SymPy tiene docenas de funciones para realizar varios tipos de simplificación, pero hay una función general llamada simplify() que intenta aplicar todas estas funciones de manera inteligente para llegar a la forma más simple de una expresión. Aquí hay unos ejemplos:

Es importante tener esta definición en cuenta cuando se presentan igualdades entre expresiones.

simplify(sin(x)**2 + cos(x)**2)
\[\displaystyle 1\]
(x**3 + x**2 - x - 1)/(x**2 + 2*x + 1)
\[\displaystyle \frac{x^{3} + x^{2} - x - 1}{x^{2} + 2 x + 1}\]
simplify((x**3 + x**2 - x - 1)/(x**2 + 2*x + 1))
\[\displaystyle x - 1\]
simplify(gamma(x)/gamma(x - 2))
\[\displaystyle \left(x - 2\right) \left(x - 1\right)\]
simplify (x**2+2*x+1 - (x+1)**2)
\[\displaystyle 0\]

Un problema de simplify() es que puede ser innecesariamente lento, ya que intenta muchos tipos de simplificaciones antes de elegir la mejor. Si ya sabe exactamente qué tipo de simplificación está buscando, es mejor aplicar las funciones de simplificación específicas que aplican esas simplificaciones.

Simplificación de funciones polinómicas racionales#

expand() es una de las funciones de simplificación más comunes en SymPy. Aunque tiene muchos ámbitos, por ahora, consideraremos su función en la expansión de expresiones polinómicas. Por ejemplo:

expand((x+2)**3)
\[\displaystyle x^{3} + 6 x^{2} + 12 x + 8\]

factor() toma un polinomio y lo factoriza en factores irreducibles sobre los números racionales. Por ejemplo:

factor(x**3-x**2+x-1)
\[\displaystyle \left(x - 1\right) \left(x^{2} + 1\right)\]
factor_list(x**3-x**2+x-1)
(1, [(x - 1, 1), (x**2 + 1, 1)])

cancel() tomará cualquier función racional y la pondrá en la forma canónica estándar, 𝑝/𝑞, dónde 𝑝 y 𝑞 son polinomios expandidos sin factores comunes, y los coeficientes principales de 𝑝 y 𝑞 no tienen denominadores (es decir, son enteros).

cancel((x**2 + 2*x + 1)/(x**2 + x))
\[\displaystyle \frac{x + 1}{x}\]
expr = (x*y**2 - 2*x*y*z + x*z**2 + y**2 - 2*y*z + z**2)/(x**2 - 1)
expr
\[\displaystyle \frac{x y^{2} - 2 x y z + x z^{2} + y^{2} - 2 y z + z^{2}}{x^{2} - 1}\]
cancel(expr)
\[\displaystyle \frac{y^{2} - 2 y z + z^{2}}{x - 1}\]

apart() realiza una descomposición en fracciones parciales de una función racional.

expr = (4*x**3 + 21*x**2 + 10*x + 12)/(x**4 + 5*x**3 + 5*x**2 + 4*x)
expr
\[\displaystyle \frac{4 x^{3} + 21 x^{2} + 10 x + 12}{x^{4} + 5 x^{3} + 5 x^{2} + 4 x}\]
apart(expr)
\[\displaystyle \frac{2 x - 1}{x^{2} + x + 1} - \frac{1}{x + 4} + \frac{3}{x}\]

Para simplificar expresiones usando identidades trigonométricas, es recomendable el uso de trigsimp().

trigsimp(sin(x)*tan(x)/sec(x))
\[\displaystyle \sin^{2}{\left(x \right)}\]
cancel(sin(x)*tan(x)/sec(x))
\[\displaystyle \frac{\sin{\left(x \right)} \tan{\left(x \right)}}{\sec{\left(x \right)}}\]

Para expandir las funciones trigonométricas, es decir, aplicar la suma o las identidades de doble ángulo, usamos expand_trig().

expand_trig(sin(x + y))
\[\displaystyle \sin{\left(x \right)} \cos{\left(y \right)} + \sin{\left(y \right)} \cos{\left(x \right)}\]
expand_trig(tan(2*x))
\[\displaystyle \frac{2 \tan{\left(x \right)}}{1 - \tan^{2}{\left(x \right)}}\]

powsimp() permite simplificaciones con leyes de exponentes.

x, y = symbols('x y', positive=True)

a, b = symbols('a b', real=True)

z, t, c = symbols('z t c')
powsimp(x**a*x**b)
\[\displaystyle x^{a + b}\]
powsimp(z**t*z**c)
\[\displaystyle z^{c + t}\]

Con rewrite() podemos reescribir algunas funciones especiales en términos de otra:

tan(x).rewrite(sin)
\[\displaystyle \frac{2 \sin^{2}{\left(x \right)}}{\sin{\left(2 x \right)}}\]
factorial(x).rewrite(gamma)
\[\displaystyle \Gamma\left(x + 1\right)\]
exp(I*x).rewrite(sin)
\[\displaystyle i \sin{\left(x \right)} + \cos{\left(x \right)}\]

Fracciones Continuas

Una fracción continua es una expresión de la forma:

\[a_0 + \cfrac{1}{a_1 + \cfrac{1}{a_2 + \cfrac{1}{ \ddots + \cfrac{1}{a_n} }}}\]

Realizar una función en sympy que admita una lista \([a_0,a_1,a_2,\cdots,a_n]\) y que retorne el valor obtenido por la fracción.

L=[1,2,3]
L.reverse()
L
[3, 2, 1]
def lista_a_fraccion(L):
    L.reverse()
    expr=Integer(0)
    for i in L:
        expr=1/(expr+i)
    return expr
lista_a_fraccion([x,y,z])
\[\displaystyle \frac{1}{x + \frac{1}{y + \frac{1}{z}}}\]
lista_a_fraccion([1,2*x,3])
\[\displaystyle \frac{1}{1 + \frac{1}{2 x + \frac{1}{3}}}\]

Cálculo#

SymPy también permite realizar tareas básicas del cálculo diferencial e integral.

from sympy import *
x, y, z = symbols('x y z')

Derivadas#

diff(cos(x),x) #derivadas en una sola variable
\[\displaystyle - \sin{\left(x \right)}\]
diff(exp(4*x**2+5*x),x) #derivadas en una sola variable
\[\displaystyle \left(8 x + 5\right) e^{4 x^{2} + 5 x}\]
diff(y*exp(4*x**2+5*y),x) #derivadas parciales
\[\displaystyle 8 x y e^{4 x^{2} + 5 y}\]
diff(cos(x),x,x)  #derivadas de orden superior
\[\displaystyle - \cos{\left(x \right)}\]
diff(cos(x),x,x,x)
\[\displaystyle \sin{\left(x \right)}\]
diff(cos(x),x,3) 
\[\displaystyle \sin{\left(x \right)}\]
diff(y*exp(4*x**2+5*y),x,y) #derivadas de orden superior
\[\displaystyle 8 x \left(5 y + 1\right) e^{4 x^{2} + 5 y}\]

Como en las ecuaciones, a veces requerimos derivadas expresadas, sin evaluarse:

deriv = Derivative( exp (x * y * z), x, y, y, z)
deriv
\[\displaystyle \frac{\partial^{4}}{\partial z\partial y^{2}\partial x} e^{x y z}\]

Para evaluarla:

deriv.doit()
\[\displaystyle x z \left(x^{2} y^{2} z^{2} + 5 x y z + 4\right) e^{x y z}\]
diff( exp (x * y * z), x, y, y, z)
\[\displaystyle x z \left(x^{2} y^{2} z^{2} + 5 x y z + 4\right) e^{x y z}\]

Integrales#

integrate(cos(x),x) # Integrales
\[\displaystyle \sin{\left(x \right)}\]
integrate(exp (-x), (x, 0, oo))
\[\displaystyle 1\]
Integral(exp (-x), (x, 0, oo))
\[\displaystyle \int\limits_{0}^{\infty} e^{- x}\, dx\]
Integral(exp (-x), (x, 0, oo)).doit()
\[\displaystyle 1\]
integrate(x**2, (x, 0,1))
\[\displaystyle \frac{1}{3}\]
Integral(exp(-x**2 - y**2), (x, -oo, oo), (y, -oo, oo))
\[\displaystyle \int\limits_{-\infty}^{\infty}\int\limits_{-\infty}^{\infty} e^{- x^{2} - y^{2}}\, dx\, dy\]
integrate(exp(-x**2 - y**2), (x, -oo, oo), (y, -oo, oo))
\[\displaystyle \pi\]
Integral(exp(-x**2 - y**2), (x, -oo, oo), (y, -oo, oo)).doit()
\[\displaystyle \pi\]

Y ¿si la integral no es posible de calcular?

integrate(x**x, x)
\[\displaystyle \int x^{x}\, dx\]

Para expresar una integral no evaluada usamos Integral:

integ = Integral((x**4 + x**2*exp(x) - x**2 - 2*x*exp(x) - 2*x -exp(x))*exp(x)/((x - 1)**2*(x + 1)**2*(exp(x) + 1)), x)
integ
\[\displaystyle \int \frac{\left(x^{4} + x^{2} e^{x} - x^{2} - 2 x e^{x} - 2 x - e^{x}\right) e^{x}}{\left(x - 1\right)^{2} \left(x + 1\right)^{2} \left(e^{x} + 1\right)}\, dx\]
integ.doit()
\[\displaystyle \log{\left(e^{x} + 1 \right)} + \frac{e^{x}}{x^{2} - 1}\]

Límites#

limit(sin(x)/x, x, 0) #Límites
\[\displaystyle 1\]
expr = sin(x)/x
expr.subs(x, 0)
\[\displaystyle \text{NaN}\]
limit(expr, x, 0)
\[\displaystyle 1\]
expr = Limit((cos(x) - 1)/x, x, 0,"-") #Límites sin evaluar
expr
\[\displaystyle \lim_{x \to 0^-}\left(\frac{\cos{\left(x \right)} - 1}{x}\right)\]
expr.doit()
\[\displaystyle 0\]
expr = exp(sin(x)) 
expr.series(x,0, 10)  # Series
\[\displaystyle 1 + x + \frac{x^{2}}{2} - \frac{x^{4}}{8} - \frac{x^{5}}{15} - \frac{x^{6}}{240} + \frac{x^{7}}{90} + \frac{31 x^{8}}{5760} + \frac{x^{9}}{5670} + O\left(x^{10}\right)\]
exp(x - 6).series(x, 6,10) # Series no centradas en 0
\[\displaystyle -5 + \frac{\left(x - 6\right)^{2}}{2} + \frac{\left(x - 6\right)^{3}}{6} + \frac{\left(x - 6\right)^{4}}{24} + \frac{\left(x - 6\right)^{5}}{120} + \frac{\left(x - 6\right)^{6}}{720} + \frac{\left(x - 6\right)^{7}}{5040} + \frac{\left(x - 6\right)^{8}}{40320} + \frac{\left(x - 6\right)^{9}}{362880} + x + O\left(\left(x - 6\right)^{10}; x\rightarrow 6\right)\]

Solucionar ecuaciones#

Ahora hagamos una rápida exploración por las diferentes ecuaciones que se resuelven en Sympy.

Eq(x**2,1)
\[\displaystyle x^{2} = 1\]
solveset(Eq(x**2,1),x)
\[\displaystyle \left\{-1, 1\right\}\]
solveset(Eq(x**2,1),x)[0]
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-94-bf4836c147c4> in <module>
----> 1 solveset(Eq(x**2,1),x)[0]

TypeError: 'FiniteSet' object is not subscriptable
list(solveset(Eq(x**2,1),x))[0]
\[\displaystyle -1\]
solveset(x ** 2 - x, x) # Raices
\[\displaystyle \left\{0, 1\right\}\]
solveset(x - x, x)
\[\displaystyle \mathbb{C}\]
solveset(x - x, x,domain=sp.Integers)
\[\displaystyle \mathbb{Z}\]
solveset(x - x, x,domain=Interval(0,10))
\[\displaystyle \left[0, 10\right]\]
solveset(x - x, x,domain=Interval.Ropen(0,10))
\[\displaystyle \left[0, 10\right)\]
solveset(x - x, x,domain=Interval.Lopen(0,10))
\[\displaystyle \left(0, 10\right]\]
solveset(x - x, x,domain=Interval.open(0,10))
\[\displaystyle \left(0, 10\right)\]
solveset(x - x, x,domain=FiniteSet(0,1,2,3))
\[\displaystyle \left\{0, 1, 2, 3\right\}\]
solveset(sin(x) - 1, x, domain=sp.Reals)
\[\displaystyle \left\{2 n \pi + \frac{\pi}{2}\; \middle|\; n \in \mathbb{Z}\right\}\]
solveset(sin(x) - 1, x, domain=Interval.open(5,34))
\[\displaystyle \left\{\frac{5 \pi}{2}, \frac{9 \pi}{2}, \frac{13 \pi}{2}, \frac{17 \pi}{2}, \frac{21 \pi}{2}\right\}\]
solveset(exp(x), x)     # Cuando la solución no existe
\[\displaystyle \emptyset\]
solveset(cos(x) - x, x)  # Cuando es incapaz de encontrar una solución
\[\displaystyle \left\{x\; \middle|\; x \in \mathbb{C} \wedge - x + \cos{\left(x \right)} = 0 \right\}\]

Sistemas de ecuaciones lineales#

Usamos linsolve para encontrar las soluciones:

linsolve([x + y + z - 1, x + y + 2*z - 3 ], (x, y, z))
\[\displaystyle \left\{\left( - y - 1, \ y, \ 2\right)\right\}\]
linsolve([x + y + z - 1, x - y + z - 3,x+y+1 ], (x, y, z))
\[\displaystyle \left\{\left( 0, \ -1, \ 2\right)\right\}\]
linsolve([x + y + z - 1, x + y + z - 3,x+y+1 ], (x, y, z))
\[\displaystyle \emptyset\]

Sistemas no lineales#

Usaremos nonlinsolve

a, b, c, d = symbols('a, b, c, d', real=True)
nonlinsolve([a**2 + a, a - b], [a, b])
\[\displaystyle \left\{\left( -1, \ -1\right), \left( 0, \ 0\right)\right\}\]
nonlinsolve([x*y - 1, x - 2], x, y)
\[\displaystyle \left\{\left( 2, \ \frac{1}{2}\right)\right\}\]