Andaba yo preguntándome esta mañana, a falta (¡sorprendentemente!) de dudas de los lectores:
Si tengo un número complejo en SymPy, ¿cómo puedo separar la parte real y la parte imaginaria? Y ya puestos, ¿puedo separar también el módulo y el argumento?
Si utilizamos el tipo complex
de Python el resultado es correcto pero puede no ser demasiado vistoso:
1 2 3 4 |
$ isympy In [1]: (1j + 1) / (5 - 2j) Out[1]: (0.10344827586206896+0.24137931034482757j) |
Queremos usar las capacidades simbólicas de SymPy. En SymPy, como se indica en el tutorial, los complejos se declaran de esta manera:
1 2 3 4 5 |
In [2]: (1 * I + 1) / (5 - 2 * I) Out[2]: 1 + ⅈ ─────── 5 - 2⋅ⅈ |
Y ya tenemos un objeto de SymPy con toda su potencia (que además se imprime bonito). Para extraer la parte real e imaginaria podemos usar las funciones re
e im
o el método as_real_imag
.
1 2 3 4 5 6 7 |
In [3]: a = (1 * I + 1) / (5 - 2 * I) In [4]: re(a) Out[4]: 3/29 In [5]: im(a) Out[5]: 7/29 In [6]: a.as_real_imag() Out[6]: (3/29, 7/29) |
Estos métodos extraen la parte real y la imaginaria pero «pierdo» el número original. Para reescribir el número separando parte real e imaginaria lo mejor es emplear el método expand(complex=True)
:
1 2 3 4 5 |
In [9]: a.expand(complex=True) Out[9]: 3 7⋅ⅈ ── + ─── 29 29 |
Esto ya es otra cosa 😉
¿Y qué pasa si tenemos una expresión simbólica? También podemos separarla, pero en este caso hay que tener cuidado con la definición de los símbolos:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
In [12]: f = 1 / (x + I * y) In [13]: f Out[13]: 1 ─────── x + ⅈ⋅y In [14]: f.expand(complex=True) Out[14]: re(x) ───────────────────────────────────────────────────────────────── - ────────── 2 2 2 2 2 re (x) - 2⋅re(x)⋅im(y) + re (y) + 2⋅re(y)⋅im(x) + im (x) + im (y) re (x) - 2 ⅈ⋅re(y) ─────────────────────────────────────────────────────── - ──────────────────── 2 2 2 2 ⋅re(x)⋅im(y) + re (y) + 2⋅re(y)⋅im(x) + im (x) + im (y) re (x) - 2⋅re(x)⋅im( ⅈ⋅im(x) ───────────────────────────────────────────── - ────────────────────────────── 2 2 2 2 2 y) + re (y) + 2⋅re(y)⋅im(x) + im (x) + im (y) re (x) - 2⋅re(x)⋅im(y) + re (y im(y) ─────────────────────────────────── 2 2 ) + 2⋅re(y)⋅im(x) + im (x) + im (y) |
¿Qué demonios? El problema es que, por defecto, los símbolos en SymPy pertenecen al cuerpo de los números complejos:
1 2 |
In [25]: x.expand(complex=True) Out[25]: re(x) + ⅈ⋅im(x) |
Observa de hecho que algunas simplificaciones no se llevarán a cabo, como se explica en la documentación:
1 2 3 4 5 |
In [26]: sqrt(x ** 2) Out[26]: ╱ 2 ╲╱ x |
Pero podemos crear símbolos con las propiedades (en SymPy assumptions) que nosotros queramos:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
In [29]: x, y = symbols("x, y", real=True) In [30]: f = 1 / (x + I * y) In [31]: f Out[31]: 1 ─────── x + ⅈ⋅y In [32]: f.expand(complex=True) # ¡Mucho mejor! Out[32]: x ⅈ⋅y ─────── - ─────── 2 2 2 2 x + y x + y In [33]: x.assumptions0 Out[33]: {'commutative': True, 'complex': True, 'hermitian': True, 'imaginary': False, 'real': True} In [34]: x.is_real Out[34]: True |
Para hallar el módulo y el argumento, empleamos las funciones abs
y arg
, aunque tal vez haga falta expandir la expresión primero:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
In [47]: abs(f) Out[47]: │ 1 │ │───────│ │x + ⅈ⋅y│ In [48]: abs(f.expand(complex=True)) Out[48]: ╱ 2 2 ╱ x y ╱ ────────── + ────────── ╱ 2 2 ╱ ⎛ 2 2⎞ ⎛ 2 2⎞ ╲╱ ⎝x + y ⎠ ⎝x + y ⎠ In [49]: arg(f.expand(complex=True)) # No funciona :( Out[49]: ⎛ x ⅈ⋅y ⎞ arg⎜─────── - ───────⎟ ⎜ 2 2 2 2⎟ ⎝x + y x + y ⎠ In [56]: a Out[56]: 1 + ⅈ ─────── 5 - 2⋅ⅈ In [54]: abs(a) Out[54]: <em>_</em> │ 1 │ ╲╱ 2 ⋅│───────│ │5 - 2⋅ⅈ│ In [55]: abs(a.expand(complex=True)) Out[55]: ╲╱ 58 ────── 29 In [57]: arg(a) Out[57]: atan(7/3) |
Si especificamos las propiedades adecuadas, las simplificaciones se efectúan correctamente:
1 2 |
In [35]: sqrt(x ** 2) Out[35]: │x│ |
Quien esperase x
como resultado tiene que repasar matemáticas 😉
También podemos utilizar el sistema de assumptions de SymPy (¿cómo traduciría esto?):
1 2 3 4 5 6 7 8 |
In [38]: refine(sqrt(z ** 2), Q.real(z)) Out[38]: │z│ In [39]: refine(sqrt(z ** 2), Q.positive(z)) Out[39]: z In [41]: with assuming(Q.positive(z)): ….: print(ask(Q.real(sqrt(z)))) ….: True |
Y esto ya no responde a la pregunta de la semana, ¡pero seguro que resulta útil!
¡Recuerda mandarnos tu pregunta para que la contestemos semanalmente en Pybonacci! Un saludo 😉