# Introducción a Sympy

Ademais das variables numéricas existen as variables simbólicas que permiten calcular
límites, derivadas, integrais etc., como se fai habitualmente nas clases de matemáticas.
Para poder facer estas operacións, habituais nun curso de Cálculo, é preciso ter instalada a libraría **Sympy**.

Ao contrario que o módulo **Math** ou o módulo **Numpy** que acabamos de revisar na práctica anterior, o módulo **Sympy** non traballa cunha estrutura de datos baseado en números (xa sexan de tipo enteiro ou dobre) senón que traballa con obxectos que posúen atributos e métodos que tratan de reproducir o comportamento matemático de variables, funcións, rexións, ecuacións, etc. coas que se traballa habitualmente nas disciplinas da álxebra e o cálculo diferencial e integral.

Para empregar directamente este guión de prácticas dende unha instalación de Python con *Anaconda*, basta con facer clic na aplicación 'Jupyter notebook' que xa está instalada por defecto (para máis detalles: https://jupyter-notebook-beginner-guide.readthedocs.io/en/latest/execute.html).

### Obxectivos:

- Uso de variables simbólicas
- Suposicións e requerimentos das variables 
- Manipulación de expresións sinxelas en varias variables


## Instalación e carga do módulo
Para facer que estea dispoñible o módulo **Sympy**, hai que instalalo usando a ferramente `pip` (ou `conda` se estades a usar entornos de traballo diferenciados). No caso do uso de *Microsoft Azute Notebooks* (https://notebooks.azure.com/), empregaríase a seguinte instalación:


```python
!pip -q install sympy
```

    [33mYou are using pip version 19.3.1, however version 20.0.2 is available.
    You should consider upgrading via the 'pip install --upgrade pip' command.[0m


Para dispoñer do módulo **Sympy** e importalo para o resto do guión de prácticas, usaremos:


```python
import sympy as sp
```

## Variables simbólicas
Para traballar en modo simbólico é necesario definir variables simbólicas e para facer
isto usaremos o función `sp.Symbol`. Vexamos algúns exemplos do seu uso:


```python
x = sp.Symbol('x') # define a variable simbólica x
y = sp.Symbol('y') # define a variable simbólica y
f = 3*x + 5*y # agora temos definida a expresion simbólica f
print(f)

a, b, c = sp.symbols('a:c') # define como simbólicas as variables a, b, c.
expresion = a**3 + b**2 + c
print(expresion)
```

    3*x + 5*y
    a**3 + b**2 + c


Por claridade na implementación e nos cálculos, será habitual que o nome da variable simbólica e o nome do obxecto **Sympy** no que se alamacena coincidan, pero isto non ter porque ser así:


```python
a = sp.Symbol('x')
print(a)
a.name
```

    x





    'x'



Debemos ter claso que agora as variables `x` ou `y` definidas antes non son números, nin tampouco pertencen aos obxectos definidos co módulo **Numpy** revisado na práctica anterior. Todas as variables simbólicas son obxectos da clase `sp.Symbol` e os seus atributos e métodos son completamente diferentes aos que aparecían ás variables numéricas e vectores de **Numpy**:


```python
print(type(x))
dir(x)
```

    <class 'sympy.core.symbol.Symbol'>





    ['_Symbol__xnew_cached_',
     '__abs__',
     '__add__',
     '__and__',
     '__call__',
     '__class__',
     '__complex__',
     '__delattr__',
     '__div__',
     '__doc__',
     '__eq__',
     '__float__',
     '__format__',
     '__ge__',
     '__getattribute__',
     '__getnewargs__',
     '__getstate__',
     '__gt__',
     '__hash__',
     '__init__',
     '__int__',
     '__invert__',
     '__le__',
     '__long__',
     '__lshift__',
     '__lt__',
     '__mod__',
     '__module__',
     '__mul__',
     '__ne__',
     '__neg__',
     '__new__',
     '__new_stage2__',
     '__or__',
     '__pos__',
     '__pow__',
     '__radd__',
     '__rand__',
     '__rdiv__',
     '__reduce__',
     '__reduce_ex__',
     '__repr__',
     '__rlshift__',
     '__rmod__',
     '__rmul__',
     '__ror__',
     '__rpow__',
     '__rrshift__',
     '__rshift__',
     '__rsub__',
     '__rtruediv__',
     '__rxor__',
     '__setattr__',
     '__setstate__',
     '__sizeof__',
     '__slots__',
     '__str__',
     '__sub__',
     '__subclasshook__',
     '__truediv__',
     '__xnew__',
     '__xor__',
     '_args',
     '_assumptions',
     '_compare_pretty',
     '_diff_wrt',
     '_eval_adjoint',
     '_eval_as_leading_term',
     '_eval_conjugate',
     '_eval_derivative',
     '_eval_evalf',
     '_eval_expand_complex',
     '_eval_interval',
     '_eval_is_algebraic_expr',
     '_eval_is_negative',
     '_eval_is_polynomial',
     '_eval_is_positive',
     '_eval_is_rational_function',
     '_eval_lseries',
     '_eval_nseries',
     '_eval_power',
     '_eval_rewrite',
     '_eval_simplify',
     '_eval_subs',
     '_eval_transpose',
     '_evalf',
     '_expand_hint',
     '_explicit_class_assumptions',
     '_from_mpmath',
     '_has',
     '_has_matcher',
     '_hashable_content',
     '_mhash',
     '_op_priority',
     '_parse_order',
     '_prop_handler',
     '_random',
     '_recursive_call',
     '_sage_',
     '_sanitize',
     '_sorted_args',
     '_subs',
     '_to_mpmath',
     'adjoint',
     'apart',
     'args',
     'args_cnc',
     'as_base_exp',
     'as_coeff_Add',
     'as_coeff_Mul',
     'as_coeff_add',
     'as_coeff_exponent',
     'as_coeff_mul',
     'as_coefficient',
     'as_coefficients_dict',
     'as_content_primitive',
     'as_dummy',
     'as_expr',
     'as_independent',
     'as_leading_term',
     'as_numer_denom',
     'as_ordered_factors',
     'as_ordered_terms',
     'as_poly',
     'as_powers_dict',
     'as_real_imag',
     'as_terms',
     'assumptions0',
     'atoms',
     'cancel',
     'canonical_variables',
     'class_key',
     'coeff',
     'collect',
     'combsimp',
     'compare',
     'compute_leading_term',
     'conjugate',
     'copy',
     'could_extract_minus_sign',
     'count',
     'count_ops',
     'default_assumptions',
     'diff',
     'doit',
     'dummy_eq',
     'equals',
     'evalf',
     'expand',
     'extract_additively',
     'extract_branch_factor',
     'extract_multiplicatively',
     'factor',
     'find',
     'free_symbols',
     'fromiter',
     'func',
     'getO',
     'getn',
     'has',
     'integrate',
     'invert',
     'is_Add',
     'is_AlgebraicNumber',
     'is_Atom',
     'is_Boolean',
     'is_Derivative',
     'is_Dummy',
     'is_Equality',
     'is_Float',
     'is_Function',
     'is_Integer',
     'is_Matrix',
     'is_Mul',
     'is_Not',
     'is_Number',
     'is_NumberSymbol',
     'is_Order',
     'is_Piecewise',
     'is_Poly',
     'is_Pow',
     'is_Rational',
     'is_Relational',
     'is_Symbol',
     'is_Vector',
     'is_Wild',
     'is_algebraic',
     'is_algebraic_expr',
     'is_antihermitian',
     'is_bounded',
     'is_commutative',
     'is_comparable',
     'is_complex',
     'is_composite',
     'is_constant',
     'is_even',
     'is_finite',
     'is_hermitian',
     'is_hypergeometric',
     'is_imaginary',
     'is_infinite',
     'is_infinitesimal',
     'is_integer',
     'is_irrational',
     'is_negative',
     'is_noninteger',
     'is_nonnegative',
     'is_nonpositive',
     'is_nonzero',
     'is_number',
     'is_odd',
     'is_polar',
     'is_polynomial',
     'is_positive',
     'is_prime',
     'is_rational',
     'is_rational_function',
     'is_real',
     'is_transcendental',
     'is_unbounded',
     'is_zero',
     'iter_basic_args',
     'leadterm',
     'limit',
     'lseries',
     'match',
     'matches',
     'n',
     'name',
     'normal',
     'nseries',
     'nsimplify',
     'powsimp',
     'primitive',
     'radsimp',
     'ratsimp',
     'rcall',
     'refine',
     'removeO',
     'replace',
     'rewrite',
     'round',
     'separate',
     'series',
     'simplify',
     'sort_key',
     'subs',
     'taylor_term',
     'together',
     'transpose',
     'trigsimp',
     'xreplace']



Con **Sympy** pódense definir constantes enteiras ou números racioanais (todas de forma simbólica) de xeito doado usando o comando `sp.Integer` ou `sp.Rational`. Por exemplo, podemos definir a constante simbólica $1/3$. Se fixeramos o mesmo con números representados por defecto en Python, obteríamos resultados moi diferentes. Observa tamén a diferenza que existe entre o tipo
de dato asignado no espazo de traballo


```python
a = sp.Rational('1/3')
b = sp.Integer('6')/sp.Integer('3')
c = 1/3
d = 1.0/3.0
print(a)
print(b)
print(c)
print(d)
print(type(a))
print(type(b))
print(type(c))
print(type(d))
print(a)
print(b)
```

    1/3
    2
    0
    0.333333333333
    <class 'sympy.core.numbers.Rational'>
    <class 'sympy.core.numbers.Integer'>
    <type 'int'>
    <type 'float'>
    1/3
    2


Outra forma sinxela de manexar valores constante mediante obxectos do módulo **Sympy** é usar a función `sp.S`. Unha vez feitos todos os cálculos simbólicos, se precisamos obter o valor numérico, empregaríase a función `sp.N` ou ben directamente `float`:


```python
a = sp.S(2)
b = sp.S(6)
c = a/b
d = sp.N(c)
e = float(c)
print(type(a))
print(type(b))
print(type(c))
print(type(d))
print(type(e))
print(c)
print(d)
print('{0:.15f}'.format(e))
```

    <class 'sympy.core.numbers.Integer'>
    <class 'sympy.core.numbers.Integer'>
    <class 'sympy.core.numbers.Rational'>
    <class 'sympy.core.numbers.Float'>
    <type 'float'>
    1/3
    0.333333333333333
    0.333333333333333


Ao longo do curso usaremos asiduamente dous números reais que podes definir como constantes simbólicas: $\pi$ e o numéro $e$. Do mesmo xeito, para operar con variables ou constantes simbólicas, debemos empregar funcións que sexan capaces de manipular este tipo de obxectos, todas elas implementadas no módulo **Sympy** (por exemplo, `sp.sin`, `sp.cos`, `sp.log`, etc)


```python
import numpy as np
print(np.pi)
print(type(np.pi))

p=sp.pi # definición da constante pi
print(sp.cos(p))

e = sp.E # definición do número e
print(sp.log(e))

print(sp.N(sp.pi,1000))
print(type(sp.N(sp.pi,100)))
```

    3.14159265359
    <type 'float'>
    -1
    1
    3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117067982148086513282306647093844609550582231725359408128481117450284102701938521105559644622948954930381964428810975665933446128475648233786783165271201909145648566923460348610454326648213393607260249141273724587006606315588174881520920962829254091715364367892590360011330530548820466521384146951941511609433057270365759591953092186117381932611793105118548074462379962749567351885752724891227938183011949129833673362440656643086021394946395224737190702179860943702770539217176293176752384674818467669405132000568127145263560827785771342757789609173637178721468440901224953430146549585371050792279689258923542019956112129021960864034418159813629774771309960518707211349999998372978049951059731732816096318595024459455346908302642522308253344685035261931188171010003137838752886587533208381420617177669147303598253490428755468731159562863882353787593751957781857780532171226806613001927876611195909216420198
    <class 'sympy.core.numbers.Float'>


## Suposicións sobre as variables

Cando se define unha variable simbólica se lle pode asignar certa información adicional sobre o tipo de valores que pode acadar, ou as suposicións que se lle van a aplicar. Por exemplo, podemos decidir antes de facer calquera cálculo se a variable toma valores enteiros ou reais, se é positiva ou negativa, maior que un certo número, etc. Este tipo de información engádese no momento da definición da variable simbólica como un argumento opcional.


```python
x = sp.Symbol('x', nonnegative = True) # A raíz cadrada dun número non negativo é real
y = sp.sqrt(x)
print(y.is_real)

x = sp.Symbol('x', integer = True) # A potencia dun número enteiro é enteira
y = x**sp.S(2)
print(y.is_integer)

a = sp.Symbol('a')
b = sp.sqrt(a)
print(b.is_real)

a = sp.Symbol('a')
b = a**sp.S(2)
print(b.is_integer)
```

    True
    True
    None
    None


Posto que os cálculos simbólicos son consistentes en **Sympy**, se poden tamén facer comprobacións sobre se algunhas desigualdades son certas ou non, sempre e cando se teña coidado nas suposicións que se fagan ao definir as variables simbólicas


```python
x = sp.Symbol('x', real = True)
p = sp.Symbol('p', positive = True)
q = sp.Symbol('q', real = True)
y = sp.Abs(x) + p # O valor absoluto
z = sp.Abs(x) + q
print(y > 0)
print(z > 0)
```

    True
    q + Abs(x) > 0


## Manipulación de expresións simbólicas

Do mesmo xeito que o módulo **Sympy** nos permite definir variables simbólicas, tamén podemos definir expresións matemáticas a partir destas e manipulalas, factorizándoas, expandíndoas, simplificalas, ou mesmo imprimilas dun xeito similar a como o faríamos con lápiz e papel


```python
x,y = sp.symbols('x,y', real=True)
expr = (x-3)*(x-3)**2*(y-2)
expr_long = sp.expand(expr) # Expandir expresión

print(expr_long) # Imprimir de forma estándar
sp.pprint(expr_long) # Imprimir de forma semellante a con lápiz e papel

expr_short = sp.factor(expr)
print(expr_short) # Factorizar expresión

expr = -3+(x**2-6*x+9)/(x-3)
expr_simple = sp.simplify(expr) # Simplificar expresión
sp.pprint(expr)
print(expr_simple)
```

    x**3*y - 2*x**3 - 9*x**2*y + 18*x**2 + 27*x*y - 54*x - 27*y + 54
     3        3      2         2                            
    x ⋅y - 2⋅x  - 9⋅x ⋅y + 18⋅x  + 27⋅x⋅y - 54⋅x - 27⋅y + 54
    (x - 3)**3*(y - 2)
          2          
         x  - 6⋅x + 9
    -3 + ────────────
            x - 3    
    x - 6


Dada unha expresión en **Sympy** tamén se pode manipulala, substituindo unhas variables simbólica por outras ou mesmo reemprazando as variables simbólicas por constantes. Para facer este tipo de substitucións emprégase a función `subs` e os valores a utilizar na substitución veñen definidos por un diccionario de Python:


```python
x,y = sp.symbols('x,y', real=True)
expr = x*x + x*y + y*x + y*y
res = expr.subs({x:1, y:2}) # Substitutición das variables simbólicas por constantes
print(res)

expr_sub = expr.subs({x:1-y}) # Subsitución de variable simbólica por unha expresión
sp.pprint(expr_sub)
print(sp.simplify(expr_sub))
```

    9
     2                          2
    y  + 2⋅y⋅(-y + 1) + (-y + 1) 
    1


### **Exercicio 2.1** 
Define a expresión dada pola suma dos termos seguintes:
$$
a+a^2+a^3+\ldots+a^N,
$$
onde $a$ é unha variable real arbitraria e $N$ e un valor enteiro positivo.


```python
# O TEU CÓDIGO AQUÍ
```

### **Exercicio 2.2** 
Cal é o valor exacto da anterior expresión cando $N=15$ e $a=5/6$? Cal é valor numérico en coma flotante?


```python
# O TEU CÓDIGO AQUÍ
```

e7c01f5b37f3e5ac3ee3c50f1538fd1e94426c7e
ipynb
Jupyter Notebook
practicas/introduccion-sympy.ipynb
maprieto/CalculoMultivariable
6bd7839803d696c6cd0e3536c0631453eacded70
MIT
2021-01-09T18:30:54.000Z
2021-01-09T18:30:54.000Z
practicas/introduccion-sympy.ipynb
maprieto/CalculoMultivariable
6bd7839803d696c6cd0e3536c0631453eacded70
MIT
practicas/introduccion-sympy.ipynb
maprieto/CalculoMultivariable
6bd7839803d696c6cd0e3536c0631453eacded70
MIT
Qwen/Qwen-72B
1. YES
2. YES