```python
%matplotlib inline
%config InlineBackend.figure_format = 'svg'

import scqubits as scq

import numpy as np
```

# All-in-one

```python
# parameters for the flux qubit
RATIO = 60.0
ALPHA = 0.8

flux_qubit = scq.CircuitFluxQubit(
    EJ1 = 1.0,
    EJ2 = 1.0,
    EJ3 = ALPHA*1.0,
    ECJ1 = 1.0/RATIO,
    ECJ2 = 1.0/RATIO,
    ECJ3 = 1.0/ALPHA/RATIO,
    ECg1 = 50.0/RATIO,
    ECg2 = 50.0/RATIO,
    ng1 = 0.0,
    ng2 = 0.0,
    flux = 0.4,
    ncut = 10,
)
```

# Eigenvalues and eigensystem using scqubit tools

```python
flux_qubit.plot_potential()
```

    (<Figure size 750x750 with 2 Axes>,
     <matplotlib.axes._subplots.AxesSubplot at 0x21599e24490>)

```python
vals, vecs= flux_qubit.eigensys()
```

```python
vals
#vecs_phase = np.conj(unitary).T@vecs
vals - vals[0]
```

    array([0.        , 0.26299666, 0.29339598, 0.50146767, 0.55310417,
           0.57154646])

# Eigenvalues using circuit tools

```python
flux_qubit.calculate_potentials()
vals, vecs = flux_qubit.diagonalize_phase(num_states=6)
```

```python
np.real(vals-vals[0])
```

    array([0.        , 0.26299666, 0.29339598, 0.50146767, 0.55310417,
           0.57154646])

```python
vecs.shape
```

    (21, 21, 1, 1, 1, 6)

```python
import matplotlib.pyplot as plt
vecs = np.reshape(vecs, (2*flux_qubit.ncut+1,2*flux_qubit.ncut+1,6))
plt.pcolormesh(flux_qubit.phi1.get_phase_grid(), flux_qubit.phi2.get_phase_grid(), np.abs(vecs[:,:,0])**2)
plt.colorbar()
np.sum(np.abs(vecs[:,:,0])**2)
```

    1.000000000000013

# Defining circuit topology

```python
flux_qubit = scq.Circuit()
flux_qubit.add_element(scq.Capacitance('Cg1'), ['g1', '1'])
flux_qubit.add_element(scq.Capacitance('Cg2'), ['g2', '2'])
flux_qubit.add_element(scq.Capacitance('CJ1'), ['GND', '1'])
flux_qubit.add_element(scq.Capacitance('CJ2'), ['GND', '2'])
flux_qubit.add_element(scq.Capacitance('CJ3'), ['1', '3'])
flux_qubit.add_element(scq.JosephsonJunction('J1', use_offset=False), ['GND', '1'])
flux_qubit.add_element(scq.JosephsonJunction('J2', use_offset=False), ['GND', '2'])
flux_qubit.add_element(scq.JosephsonJunction('J3', use_offset=False), ['1', '3'])
```

# Parametrizing Hamiltonian

```python
ϕ1 = scq.Variable('ϕ_1')
ϕ2 = scq.Variable('ϕ_2')
f =  scq.Variable('f')
g1 = scq.Variable('g_1')
g2 = scq.Variable('g_2')

flux_qubit.add_variable(ϕ1)
flux_qubit.add_variable(ϕ2)
flux_qubit.add_variable(f)
flux_qubit.add_variable(g1)
flux_qubit.add_variable(g2)
```

# Mapping nodal phases to variables

```python
flux_qubit.map_nodes_linear(['GND', '1', '2', '3', 'g1', 'g2'],
                            ['ϕ_1', 'ϕ_2', 'f', 'g_1', 'g_2'],
                            np.asarray([[0,   0,   0,   0,   0],
                                        [1,   0,   0,   0,   0],
                                        [0,   1,   0,   0,   0],
                                        [0,   1,  -1,   0,   0],
                                        [0,   0,   0,   1,   0],
                                        [0,   0,   0,   0,   1]]))
```

# Separating variables into external controls and degrees of freedom

```python
ϕ1.set_variable(21, 1) # 21 charge states, 2pi wavefunction periodicity
ϕ2.set_variable(21, 1) # 21 charge states, 2pi wavefunction periodicity
f.set_parameter(0.4*2*np.pi, 0) # external flux: 0.4 quantum, external voltage: 0
g1.set_parameter(0, 0) # external flux: 0 quanta, external voltage: 0
g2.set_parameter(0, 0) # external flux: 0 quanta, external voltage: 0
```

# Printing symbolic Hamiltonian (using sympy)
This part is a bit ugly, but in principle we could improve it so it uses better notation

```python
import sympy
C_g1, C_g2, C_J1, C_J2, C_J3, E_J1, E_J2, E_J3 = \
    sympy.symbols('C_g1, C_g2, C_J1, C_J2, C_J3, E_J1, E_J2, E_J3')
flux_qubit.find_element('Cg1').set_capacitance(C_g1)
flux_qubit.find_element('Cg2').set_capacitance(C_g2)
flux_qubit.find_element('CJ1').set_capacitance(C_J1)
flux_qubit.find_element('CJ2').set_capacitance(C_J2)
flux_qubit.find_element('CJ3').set_capacitance(C_J3)
flux_qubit.find_element('J1').set_critical_current(E_J1)
flux_qubit.find_element('J2').set_critical_current(E_J2)
flux_qubit.find_element('J3').set_critical_current(E_J3)
```

## capacitance matrix in terms of the nodal parameterization

```python
flux_qubit.capacitance_matrix(symbolic=True)
```

$\displaystyle \left[\begin{matrix}C_{J1} + C_{J2} & 0.0 & - C_{J1} & 0.0 & - C_{J2} & 0.0\\0.0 & C_{g1} & - C_{g1} & 0.0 & 0.0 & 0.0\\- C_{J1} & - C_{g1} & C_{J1} + C_{J3} + C_{g1} & 0.0 & 0.0 & - C_{J3}\\0.0 & 0.0 & 0.0 & C_{g2} & - C_{g2} & 0.0\\- C_{J2} & 0.0 & 0.0 & - C_{g2} & C_{J2} + C_{g2} & 0.0\\0.0 & 0.0 & - C_{J3} & 0.0 & 0.0 & C_{J3}\end{matrix}\right]$

## Legendre transform of the capacitance matrix (inverse wrt to degrees of freedom)

```python
flux_qubit.capacitance_matrix_legendre_transform(symbolic=True)
```

$\displaystyle \left[\begin{matrix}\frac{C_{J2} + C_{J3} + C_{g2}}{C_{J1} C_{J2} + C_{J1} C_{J3} + C_{J1} C_{g2} + C_{J2} C_{J3} + C_{J2} C_{g1} + C_{J3} C_{g1} + C_{J3} C_{g2} + C_{g1} C_{g2}} & \frac{C_{J3}}{C_{J1} C_{J2} + C_{J1} C_{J3} + C_{J1} C_{g2} + C_{J2} C_{J3} + C_{J2} C_{g1} + C_{J3} C_{g1} + C_{J3} C_{g2} + C_{g1} C_{g2}} & \frac{C_{J3}^{2}}{C_{J1} C_{J2} + C_{J1} C_{J3} + C_{J1} C_{g2} + C_{J2} C_{J3} + C_{J2} C_{g1} + C_{J3} C_{g1} + C_{J3} C_{g2} + C_{g1} C_{g2}} - \frac{C_{J3} \left(C_{J2} + C_{J3} + C_{g2}\right)}{C_{J1} C_{J2} + C_{J1} C_{J3} + C_{J1} C_{g2} + C_{J2} C_{J3} + C_{J2} C_{g1} + C_{J3} C_{g1} + C_{J3} C_{g2} + C_{g1} C_{g2}} & \frac{C_{g1} \left(C_{J2} + C_{J3} + C_{g2}\right)}{C_{J1} C_{J2} + C_{J1} C_{J3} + C_{J1} C_{g2} + C_{J2} C_{J3} + C_{J2} C_{g1} + C_{J3} C_{g1} + C_{J3} C_{g2} + C_{g1} C_{g2}} & \frac{C_{J3} C_{g2}}{C_{J1} C_{J2} + C_{J1} C_{J3} + C_{J1} C_{g2} + C_{J2} C_{J3} + C_{J2} C_{g1} + C_{J3} C_{g1} + C_{J3} C_{g2} + C_{g1} C_{g2}}\\\frac{C_{J3}}{C_{J1} C_{J2} + C_{J1} C_{J3} + C_{J1} C_{g2} + C_{J2} C_{J3} + C_{J2} C_{g1} + C_{J3} C_{g1} + C_{J3} C_{g2} + C_{g1} C_{g2}} & \frac{C_{J1} + C_{J3} + C_{g1}}{C_{J1} C_{J2} + C_{J1} C_{J3} + C_{J1} C_{g2} + C_{J2} C_{J3} + C_{J2} C_{g1} + C_{J3} C_{g1} + C_{J3} C_{g2} + C_{g1} C_{g2}} & - \frac{C_{J3}^{2}}{C_{J1} C_{J2} + C_{J1} C_{J3} + C_{J1} C_{g2} + C_{J2} C_{J3} + C_{J2} C_{g1} + C_{J3} C_{g1} + C_{J3} C_{g2} + C_{g1} C_{g2}} + \frac{C_{J3} \left(C_{J1} + C_{J3} + C_{g1}\right)}{C_{J1} C_{J2} + C_{J1} C_{J3} + C_{J1} C_{g2} + C_{J2} C_{J3} + C_{J2} C_{g1} + C_{J3} C_{g1} + C_{J3} C_{g2} + C_{g1} C_{g2}} & \frac{C_{J3} C_{g1}}{C_{J1} C_{J2} + C_{J1} C_{J3} + C_{J1} C_{g2} + C_{J2} C_{J3} + C_{J2} C_{g1} + C_{J3} C_{g1} + C_{J3} C_{g2} + C_{g1} C_{g2}} & \frac{C_{g2} \left(C_{J1} + C_{J3} + C_{g1}\right)}{C_{J1} C_{J2} + C_{J1} C_{J3} + C_{J1} C_{g2} + C_{J2} C_{J3} + C_{J2} C_{g1} + C_{J3} C_{g1} + C_{J3} C_{g2} + C_{g1} C_{g2}}\\\frac{C_{J3}^{2}}{C_{J1} C_{J2} + C_{J1} C_{J3} + C_{J1} C_{g2} + C_{J2} C_{J3} + C_{J2} C_{g1} + C_{J3} C_{g1} + C_{J3} C_{g2} + C_{g1} C_{g2}} - \frac{C_{J3} \left(C_{J2} + C_{J3} + C_{g2}\right)}{C_{J1} C_{J2} + C_{J1} C_{J3} + C_{J1} C_{g2} + C_{J2} C_{J3} + C_{J2} C_{g1} + C_{J3} C_{g1} + C_{J3} C_{g2} + C_{g1} C_{g2}} & - \frac{C_{J3}^{2}}{C_{J1} C_{J2} + C_{J1} C_{J3} + C_{J1} C_{g2} + C_{J2} C_{J3} + C_{J2} C_{g1} + C_{J3} C_{g1} + C_{J3} C_{g2} + C_{g1} C_{g2}} + \frac{C_{J3} \left(C_{J1} + C_{J3} + C_{g1}\right)}{C_{J1} C_{J2} + C_{J1} C_{J3} + C_{J1} C_{g2} + C_{J2} C_{J3} + C_{J2} C_{g1} + C_{J3} C_{g1} + C_{J3} C_{g2} + C_{g1} C_{g2}} & C_{J3} \left(- \frac{C_{J3}^{2}}{C_{J1} C_{J2} + C_{J1} C_{J3} + C_{J1} C_{g2} + C_{J2} C_{J3} + C_{J2} C_{g1} + C_{J3} C_{g1} + C_{J3} C_{g2} + C_{g1} C_{g2}} + \frac{C_{J3} \left(C_{J2} + C_{J3} + C_{g2}\right)}{C_{J1} C_{J2} + C_{J1} C_{J3} + C_{J1} C_{g2} + C_{J2} C_{J3} + C_{J2} C_{g1} + C_{J3} C_{g1} + C_{J3} C_{g2} + C_{g1} C_{g2}}\right) - C_{J3} \left(\frac{C_{J3}^{2}}{C_{J1} C_{J2} + C_{J1} C_{J3} + C_{J1} C_{g2} + C_{J2} C_{J3} + C_{J2} C_{g1} + C_{J3} C_{g1} + C_{J3} C_{g2} + C_{g1} C_{g2}} - \frac{C_{J3} \left(C_{J1} + C_{J3} + C_{g1}\right)}{C_{J1} C_{J2} + C_{J1} C_{J3} + C_{J1} C_{g2} + C_{J2} C_{J3} + C_{J2} C_{g1} + C_{J3} C_{g1} + C_{J3} C_{g2} + C_{g1} C_{g2}}\right) & - C_{g1} \left(- \frac{C_{J3}^{2}}{C_{J1} C_{J2} + C_{J1} C_{J3} + C_{J1} C_{g2} + C_{J2} C_{J3} + C_{J2} C_{g1} + C_{J3} C_{g1} + C_{J3} C_{g2} + C_{g1} C_{g2}} + \frac{C_{J3} \left(C_{J2} + C_{J3} + C_{g2}\right)}{C_{J1} C_{J2} + C_{J1} C_{J3} + C_{J1} C_{g2} + C_{J2} C_{J3} + C_{J2} C_{g1} + C_{J3} C_{g1} + C_{J3} C_{g2} + C_{g1} C_{g2}}\right) & - C_{g2} \left(\frac{C_{J3}^{2}}{C_{J1} C_{J2} + C_{J1} C_{J3} + C_{J1} C_{g2} + C_{J2} C_{J3} + C_{J2} C_{g1} + C_{J3} C_{g1} + C_{J3} C_{g2} + C_{g1} C_{g2}} - \frac{C_{J3} \left(C_{J1} + C_{J3} + C_{g1}\right)}{C_{J1} C_{J2} + C_{J1} C_{J3} + C_{J1} C_{g2} + C_{J2} C_{J3} + C_{J2} C_{g1} + C_{J3} C_{g1} + C_{J3} C_{g2} + C_{g1} C_{g2}}\right)\\\frac{C_{g1} \left(C_{J2} + C_{J3} + C_{g2}\right)}{C_{J1} C_{J2} + C_{J1} C_{J3} + C_{J1} C_{g2} + C_{J2} C_{J3} + C_{J2} C_{g1} + C_{J3} C_{g1} + C_{J3} C_{g2} + C_{g1} C_{g2}} & \frac{C_{J3} C_{g1}}{C_{J1} C_{J2} + C_{J1} C_{J3} + C_{J1} C_{g2} + C_{J2} C_{J3} + C_{J2} C_{g1} + C_{J3} C_{g1} + C_{J3} C_{g2} + C_{g1} C_{g2}} & \frac{C_{J3}^{2} C_{g1}}{C_{J1} C_{J2} + C_{J1} C_{J3} + C_{J1} C_{g2} + C_{J2} C_{J3} + C_{J2} C_{g1} + C_{J3} C_{g1} + C_{J3} C_{g2} + C_{g1} C_{g2}} - \frac{C_{J3} C_{g1} \left(C_{J2} + C_{J3} + C_{g2}\right)}{C_{J1} C_{J2} + C_{J1} C_{J3} + C_{J1} C_{g2} + C_{J2} C_{J3} + C_{J2} C_{g1} + C_{J3} C_{g1} + C_{J3} C_{g2} + C_{g1} C_{g2}} & \frac{C_{g1}^{2} \left(C_{J2} + C_{J3} + C_{g2}\right)}{C_{J1} C_{J2} + C_{J1} C_{J3} + C_{J1} C_{g2} + C_{J2} C_{J3} + C_{J2} C_{g1} + C_{J3} C_{g1} + C_{J3} C_{g2} + C_{g1} C_{g2}} & \frac{C_{J3} C_{g1} C_{g2}}{C_{J1} C_{J2} + C_{J1} C_{J3} + C_{J1} C_{g2} + C_{J2} C_{J3} + C_{J2} C_{g1} + C_{J3} C_{g1} + C_{J3} C_{g2} + C_{g1} C_{g2}}\\\frac{C_{J3} C_{g2}}{C_{J1} C_{J2} + C_{J1} C_{J3} + C_{J1} C_{g2} + C_{J2} C_{J3} + C_{J2} C_{g1} + C_{J3} C_{g1} + C_{J3} C_{g2} + C_{g1} C_{g2}} & \frac{C_{g2} \left(C_{J1} + C_{J3} + C_{g1}\right)}{C_{J1} C_{J2} + C_{J1} C_{J3} + C_{J1} C_{g2} + C_{J2} C_{J3} + C_{J2} C_{g1} + C_{J3} C_{g1} + C_{J3} C_{g2} + C_{g1} C_{g2}} & - \frac{C_{J3}^{2} C_{g2}}{C_{J1} C_{J2} + C_{J1} C_{J3} + C_{J1} C_{g2} + C_{J2} C_{J3} + C_{J2} C_{g1} + C_{J3} C_{g1} + C_{J3} C_{g2} + C_{g1} C_{g2}} + \frac{C_{J3} C_{g2} \left(C_{J1} + C_{J3} + C_{g1}\right)}{C_{J1} C_{J2} + C_{J1} C_{J3} + C_{J1} C_{g2} + C_{J2} C_{J3} + C_{J2} C_{g1} + C_{J3} C_{g1} + C_{J3} C_{g2} + C_{g1} C_{g2}} & \frac{C_{J3} C_{g1} C_{g2}}{C_{J1} C_{J2} + C_{J1} C_{J3} + C_{J1} C_{g2} + C_{J2} C_{J3} + C_{J2} C_{g1} + C_{J3} C_{g1} + C_{J3} C_{g2} + C_{g1} C_{g2}} & \frac{C_{g2}^{2} \left(C_{J1} + C_{J3} + C_{g1}\right)}{C_{J1} C_{J2} + C_{J1} C_{J3} + C_{J1} C_{g2} + C_{J2} C_{J3} + C_{J2} C_{g1} + C_{J3} C_{g1} + C_{J3} C_{g2} + C_{g1} C_{g2}}\end{matrix}\right]$

## Symbolic lagrangian

```python
sympy.nsimplify(flux_qubit.symbolic_lagrangian())
```

$\displaystyle E_{J1} \cos{\left(ϕ_{1} \right)} + E_{J2} \cos{\left(ϕ_{2} \right)} + E_{J3} \cos{\left(f + ϕ_{1} - ϕ_{2} \right)} + \partial_tg_1 \left(\frac{C_{g1} \partial_tg_1}{2} - \frac{C_{g1} \partial_tϕ_1}{2}\right) + \partial_tg_2 \left(\frac{C_{g2} \partial_tg_2}{2} - \frac{C_{g2} \partial_tϕ_2}{2}\right) + \partial_tϕ_1 \left(- C_{J3} \left(- \frac{\partial_tf}{2} + \frac{\partial_tϕ_2}{2}\right) - \frac{C_{g1} \partial_tg_1}{2} + \frac{\partial_tϕ_1 \left(C_{J1} + C_{J3} + C_{g1}\right)}{2}\right) + \partial_tϕ_2 \left(- \frac{C_{g2} \partial_tg_2}{2} + \frac{\partial_tϕ_2 \left(C_{J2} + C_{g2}\right)}{2}\right) + \left(- \partial_tf + \partial_tϕ_2\right) \left(- \frac{C_{J3} \partial_tϕ_1}{2} + C_{J3} \left(- \frac{\partial_tf}{2} + \frac{\partial_tϕ_2}{2}\right)\right)$

## Symbolic hamiltonian

```python
sympy.nsimplify(flux_qubit.symbolic_hamiltonian())
```

$\displaystyle - E_{J1} \cos{\left(ϕ_{1} \right)} - E_{J2} \cos{\left(ϕ_{2} \right)} - E_{J3} \cos{\left(f + ϕ_{1} - ϕ_{2} \right)} + \frac{\partial_tf \left(\partial_tf \left(C_{J3} \left(- \frac{C_{J3}^{2}}{C_{J1} C_{J2} + C_{J1} C_{J3} + C_{J1} C_{g2} + C_{J2} C_{J3} + C_{J2} C_{g1} + C_{J3} C_{g1} + C_{J3} C_{g2} + C_{g1} C_{g2}} + \frac{C_{J3} \left(C_{J2} + C_{J3} + C_{g2}\right)}{C_{J1} C_{J2} + C_{J1} C_{J3} + C_{J1} C_{g2} + C_{J2} C_{J3} + C_{J2} C_{g1} + C_{J3} C_{g1} + C_{J3} C_{g2} + C_{g1} C_{g2}}\right) - C_{J3} \left(\frac{C_{J3}^{2}}{C_{J1} C_{J2} + C_{J1} C_{J3} + C_{J1} C_{g2} + C_{J2} C_{J3} + C_{J2} C_{g1} + C_{J3} C_{g1} + C_{J3} C_{g2} + C_{g1} C_{g2}} - \frac{C_{J3} \left(C_{J1} + C_{J3} + C_{g1}\right)}{C_{J1} C_{J2} + C_{J1} C_{J3} + C_{J1} C_{g2} + C_{J2} C_{J3} + C_{J2} C_{g1} + C_{J3} C_{g1} + C_{J3} C_{g2} + C_{g1} C_{g2}}\right)\right) + \partial_tg_1 \left(\frac{C_{J3}^{2} C_{g1}}{C_{J1} C_{J2} + C_{J1} C_{J3} + C_{J1} C_{g2} + C_{J2} C_{J3} + C_{J2} C_{g1} + C_{J3} C_{g1} + C_{J3} C_{g2} + C_{g1} C_{g2}} - \frac{C_{J3} C_{g1} \left(C_{J2} + C_{J3} + C_{g2}\right)}{C_{J1} C_{J2} + C_{J1} C_{J3} + C_{J1} C_{g2} + C_{J2} C_{J3} + C_{J2} C_{g1} + C_{J3} C_{g1} + C_{J3} C_{g2} + C_{g1} C_{g2}}\right) + \partial_tg_2 \left(- \frac{C_{J3}^{2} C_{g2}}{C_{J1} C_{J2} + C_{J1} C_{J3} + C_{J1} C_{g2} + C_{J2} C_{J3} + C_{J2} C_{g1} + C_{J3} C_{g1} + C_{J3} C_{g2} + C_{g1} C_{g2}} + \frac{C_{J3} C_{g2} \left(C_{J1} + C_{J3} + C_{g1}\right)}{C_{J1} C_{J2} + C_{J1} C_{J3} + C_{J1} C_{g2} + C_{J2} C_{J3} + C_{J2} C_{g1} + C_{J3} C_{g1} + C_{J3} C_{g2} + C_{g1} C_{g2}}\right) - i \partial_{ϕ_1} \left(\frac{C_{J3}^{2}}{C_{J1} C_{J2} + C_{J1} C_{J3} + C_{J1} C_{g2} + C_{J2} C_{J3} + C_{J2} C_{g1} + C_{J3} C_{g1} + C_{J3} C_{g2} + C_{g1} C_{g2}} - \frac{C_{J3} \left(C_{J2} + C_{J3} + C_{g2}\right)}{C_{J1} C_{J2} + C_{J1} C_{J3} + C_{J1} C_{g2} + C_{J2} C_{J3} + C_{J2} C_{g1} + C_{J3} C_{g1} + C_{J3} C_{g2} + C_{g1} C_{g2}}\right) - i \partial_{ϕ_2} \left(- \frac{C_{J3}^{2}}{C_{J1} C_{J2} + C_{J1} C_{J3} + C_{J1} C_{g2} + C_{J2} C_{J3} + C_{J2} C_{g1} + C_{J3} C_{g1} + C_{J3} C_{g2} + C_{g1} C_{g2}} + \frac{C_{J3} \left(C_{J1} + C_{J3} + C_{g1}\right)}{C_{J1} C_{J2} + C_{J1} C_{J3} + C_{J1} C_{g2} + C_{J2} C_{J3} + C_{J2} C_{g1} + C_{J3} C_{g1} + C_{J3} C_{g2} + C_{g1} C_{g2}}\right)\right)}{2} + \frac{\partial_tg_1 \left(\frac{C_{J3} C_{g1} C_{g2} \partial_tg_2}{C_{J1} C_{J2} + C_{J1} C_{J3} + C_{J1} C_{g2} + C_{J2} C_{J3} + C_{J2} C_{g1} + C_{J3} C_{g1} + C_{J3} C_{g2} + C_{g1} C_{g2}} - \frac{i C_{J3} C_{g1} \partial_{ϕ_2}}{C_{J1} C_{J2} + C_{J1} C_{J3} + C_{J1} C_{g2} + C_{J2} C_{J3} + C_{J2} C_{g1} + C_{J3} C_{g1} + C_{J3} C_{g2} + C_{g1} C_{g2}} + \frac{C_{g1}^{2} \partial_tg_1 \left(C_{J2} + C_{J3} + C_{g2}\right)}{C_{J1} C_{J2} + C_{J1} C_{J3} + C_{J1} C_{g2} + C_{J2} C_{J3} + C_{J2} C_{g1} + C_{J3} C_{g1} + C_{J3} C_{g2} + C_{g1} C_{g2}} - C_{g1} \partial_tf \left(- \frac{C_{J3}^{2}}{C_{J1} C_{J2} + C_{J1} C_{J3} + C_{J1} C_{g2} + C_{J2} C_{J3} + C_{J2} C_{g1} + C_{J3} C_{g1} + C_{J3} C_{g2} + C_{g1} C_{g2}} + \frac{C_{J3} \left(C_{J2} + C_{J3} + C_{g2}\right)}{C_{J1} C_{J2} + C_{J1} C_{J3} + C_{J1} C_{g2} + C_{J2} C_{J3} + C_{J2} C_{g1} + C_{J3} C_{g1} + C_{J3} C_{g2} + C_{g1} C_{g2}}\right) - \frac{i C_{g1} \partial_{ϕ_1} \left(C_{J2} + C_{J3} + C_{g2}\right)}{C_{J1} C_{J2} + C_{J1} C_{J3} + C_{J1} C_{g2} + C_{J2} C_{J3} + C_{J2} C_{g1} + C_{J3} C_{g1} + C_{J3} C_{g2} + C_{g1} C_{g2}}\right)}{2} + \frac{\partial_tg_2 \left(\frac{C_{J3} C_{g1} C_{g2} \partial_tg_1}{C_{J1} C_{J2} + C_{J1} C_{J3} + C_{J1} C_{g2} + C_{J2} C_{J3} + C_{J2} C_{g1} + C_{J3} C_{g1} + C_{J3} C_{g2} + C_{g1} C_{g2}} - \frac{i C_{J3} C_{g2} \partial_{ϕ_1}}{C_{J1} C_{J2} + C_{J1} C_{J3} + C_{J1} C_{g2} + C_{J2} C_{J3} + C_{J2} C_{g1} + C_{J3} C_{g1} + C_{J3} C_{g2} + C_{g1} C_{g2}} + \frac{C_{g2}^{2} \partial_tg_2 \left(C_{J1} + C_{J3} + C_{g1}\right)}{C_{J1} C_{J2} + C_{J1} C_{J3} + C_{J1} C_{g2} + C_{J2} C_{J3} + C_{J2} C_{g1} + C_{J3} C_{g1} + C_{J3} C_{g2} + C_{g1} C_{g2}} - C_{g2} \partial_tf \left(\frac{C_{J3}^{2}}{C_{J1} C_{J2} + C_{J1} C_{J3} + C_{J1} C_{g2} + C_{J2} C_{J3} + C_{J2} C_{g1} + C_{J3} C_{g1} + C_{J3} C_{g2} + C_{g1} C_{g2}} - \frac{C_{J3} \left(C_{J1} + C_{J3} + C_{g1}\right)}{C_{J1} C_{J2} + C_{J1} C_{J3} + C_{J1} C_{g2} + C_{J2} C_{J3} + C_{J2} C_{g1} + C_{J3} C_{g1} + C_{J3} C_{g2} + C_{g1} C_{g2}}\right) - \frac{i C_{g2} \partial_{ϕ_2} \left(C_{J1} + C_{J3} + C_{g1}\right)}{C_{J1} C_{J2} + C_{J1} C_{J3} + C_{J1} C_{g2} + C_{J2} C_{J3} + C_{J2} C_{g1} + C_{J3} C_{g1} + C_{J3} C_{g2} + C_{g1} C_{g2}}\right)}{2} - \frac{i \partial_{ϕ_1} \left(\frac{C_{J3} C_{g2} \partial_tg_2}{C_{J1} C_{J2} + C_{J1} C_{J3} + C_{J1} C_{g2} + C_{J2} C_{J3} + C_{J2} C_{g1} + C_{J3} C_{g1} + C_{J3} C_{g2} + C_{g1} C_{g2}} - \frac{i C_{J3} \partial_{ϕ_2}}{C_{J1} C_{J2} + C_{J1} C_{J3} + C_{J1} C_{g2} + C_{J2} C_{J3} + C_{J2} C_{g1} + C_{J3} C_{g1} + C_{J3} C_{g2} + C_{g1} C_{g2}} + \frac{C_{g1} \partial_tg_1 \left(C_{J2} + C_{J3} + C_{g2}\right)}{C_{J1} C_{J2} + C_{J1} C_{J3} + C_{J1} C_{g2} + C_{J2} C_{J3} + C_{J2} C_{g1} + C_{J3} C_{g1} + C_{J3} C_{g2} + C_{g1} C_{g2}} + \partial_tf \left(\frac{C_{J3}^{2}}{C_{J1} C_{J2} + C_{J1} C_{J3} + C_{J1} C_{g2} + C_{J2} C_{J3} + C_{J2} C_{g1} + C_{J3} C_{g1} + C_{J3} C_{g2} + C_{g1} C_{g2}} - \frac{C_{J3} \left(C_{J2} + C_{J3} + C_{g2}\right)}{C_{J1} C_{J2} + C_{J1} C_{J3} + C_{J1} C_{g2} + C_{J2} C_{J3} + C_{J2} C_{g1} + C_{J3} C_{g1} + C_{J3} C_{g2} + C_{g1} C_{g2}}\right) - \frac{i \partial_{ϕ_1} \left(C_{J2} + C_{J3} + C_{g2}\right)}{C_{J1} C_{J2} + C_{J1} C_{J3} + C_{J1} C_{g2} + C_{J2} C_{J3} + C_{J2} C_{g1} + C_{J3} C_{g1} + C_{J3} C_{g2} + C_{g1} C_{g2}}\right)}{2} - \frac{i \partial_{ϕ_2} \left(\frac{C_{J3} C_{g1} \partial_tg_1}{C_{J1} C_{J2} + C_{J1} C_{J3} + C_{J1} C_{g2} + C_{J2} C_{J3} + C_{J2} C_{g1} + C_{J3} C_{g1} + C_{J3} C_{g2} + C_{g1} C_{g2}} - \frac{i C_{J3} \partial_{ϕ_1}}{C_{J1} C_{J2} + C_{J1} C_{J3} + C_{J1} C_{g2} + C_{J2} C_{J3} + C_{J2} C_{g1} + C_{J3} C_{g1} + C_{J3} C_{g2} + C_{g1} C_{g2}} + \frac{C_{g2} \partial_tg_2 \left(C_{J1} + C_{J3} + C_{g1}\right)}{C_{J1} C_{J2} + C_{J1} C_{J3} + C_{J1} C_{g2} + C_{J2} C_{J3} + C_{J2} C_{g1} + C_{J3} C_{g1} + C_{J3} C_{g2} + C_{g1} C_{g2}} + \partial_tf \left(- \frac{C_{J3}^{2}}{C_{J1} C_{J2} + C_{J1} C_{J3} + C_{J1} C_{g2} + C_{J2} C_{J3} + C_{J2} C_{g1} + C_{J3} C_{g1} + C_{J3} C_{g2} + C_{g1} C_{g2}} + \frac{C_{J3} \left(C_{J1} + C_{J3} + C_{g1}\right)}{C_{J1} C_{J2} + C_{J1} C_{J3} + C_{J1} C_{g2} + C_{J2} C_{J3} + C_{J2} C_{g1} + C_{J3} C_{g1} + C_{J3} C_{g2} + C_{g1} C_{g2}}\right) - \frac{i \partial_{ϕ_2} \left(C_{J1} + C_{J3} + C_{g1}\right)}{C_{J1} C_{J2} + C_{J1} C_{J3} + C_{J1} C_{g2} + C_{J2} C_{J3} + C_{J2} C_{g1} + C_{J3} C_{g1} + C_{J3} C_{g2} + C_{g1} C_{g2}}\right)}{2}$

```python

```


## A model for COVID 19 infection rate

ODE can be used to model change in any system.

In this example, we can use ODEs to model the infection rate for COVID-19. This is a simple model that does not include many of the details that are now known about the disease.

We consider a Susceptible-Exposed-Infectious-Removed (SEIR) model to describe the spread of the virus and compute the number of infected and dead individuals. The model is as follows

$$
\begin{align}
\frac{dS(t)}{dt} &= -\beta S(t)I(t) \\
\frac{dE(t)}{dt} &= \beta S(t)I(t) - \sigma E(t) \\
\frac{dI(t)}{dt} &= \sigma E(t) - \gamma I(t) \\
\frac{dR(t)}{dt} &= \gamma I(t)  \\
\end{align}
$$

where
- $S(t)$ -> Rate of individuals that are susceptible
- $E(t)$ -> Rate of individuals that are exposed
- $I(t)$ -> Rate of individuals that are infected
- $R(t)$ -> Rate of individuals that recovered

and
- $\beta$ -> Probability of disease transmission per contact (dimensionless)
- $\sigma$ -> Rate of progression from exposed to infectious
- $\gamma$ -> Recovery rate of infectious individuals

See:

Mwalili, Samuel, Mark Kimathi, Viona Ojiambo, Duncan Gathungu, and Rachel Mbogo. "SEIR model for COVID-19 dynamics incorporating the environment and social distancing." BMC Research Notes 13, no. 1 (2020): 1-5.

Cross, Paul C., James O. Lloyd‐Smith, Philip LF Johnson, and Wayne M. Getz. "Duelling timescales of host movement and disease recovery determine invasion of disease in structured populations." Ecology letters 8, no. 6 (2005): 587-595.

Li, Michael Y., and James S. Muldowney. "Global stability for the SEIR model in epidemiology." Mathematical biosciences 125, no. 2 (1995): 155-164.

```python
import numpy as np
from scipy.integrate import solve_ivp
import matplotlib.pyplot as plt

plt.style.use('seaborn')

def func(t,y,beta,sigma,gamma):
    s,e,i,r = y
    dsdt = -beta*s*i
    dedt = beta*s*i - sigma*e
    didt = sigma*e - gamma*i
    drdt = gamma*i

    return np.array([dsdt,dedt,didt,drdt])

h = 1
t_eval = np.arange(0, 200 + h, h)

beta = 0.8
sigma = 0.1
gamma = 0.05

i0 = 1e-2
s0 = 1-i0
e0 = 0
r0 = 0

init_cond = np.array([s0,e0,i0,r0])

sol = solve_ivp(func, [t_eval[0], t_eval[-1]], init_cond, t_eval=t_eval, args=(beta,sigma,gamma))

plt.figure(figsize = (10, 10))
plt.subplot(211)
plt.plot(sol.t, sol.y[2], label = 'Infection Rate')
plt.plot(sol.t, sol.y[3], label = 'Recovery')
plt.xlabel('time')
plt.ylabel('Infection Rate and Revovery')
plt.title('Estimation of Infection Rate and Recovery')
plt.legend()
plt.tight_layout()
plt.show()
```

```python

```


# TensorFlow Basics

Note: If running this notebook directly, make sure you are running your Jupyter kernel in an environment with TensorFlow installed. Some useful packages to install/import first, if you didn't in notebook 00A:

```python
# Beginning a line with "!" allows you to execute a command in your terminal
# You'll need to install these packages for certain lines of this notebook if you don't already have them.
!conda install -y matplotlib
!pip install -U tqdm
```

    ^C
    Collecting tqdm
      Downloading tqdm-4.15.0-py2.py3-none-any.whl (46kB)
    Installing collected packages: tqdm
      Found existing installation: tqdm 4.11.2
        Uninstalling tqdm-4.11.2:
          Successfully uninstalled tqdm-4.11.2
    Successfully installed tqdm-4.15.0

```python
%matplotlib inline

import numpy as np
import matplotlib.pyplot as plt
from tqdm import trange
```

### Basic TensorFlow test from the installation instructions

```python
import tensorflow as tf
hello = tf.constant('Hello, TensorFlow!')
sess = tf.Session()
print(sess.run(hello))
```

    b'Hello, TensorFlow!'

### Define a simple TensorFlow graph

The example from the TensorFlow Introduction slides:

```python
a = tf.constant(3.0, dtype=tf.float32)
b = tf.constant(4.0, dtype=tf.float32)
sum_a_b = tf.add(a, b)

# Using the Python "with" as a context manager
with tf.Session() as sess:
    print(sess.run(sum_a_b)) # Prints “7.0” to the screen
```

    7.0

The previous graph only produces a constant output. Not particularly interesting. To create a similar graph that can acccept variable inputs:

```python
a = tf.placeholder(tf.float32)
b = tf.placeholder(tf.float32)
sum_a_b = tf.add(a, b)

with tf.Session() as sess:
    feed_dict = {a: 3.0, b: 4.0}
    print('1st Result: {0}'.format(sess.run(sum_a_b, feed_dict=feed_dict)))

    feed_dict = {a: 2017, b: 2020}
    print('2nd Result: {0}'.format(sess.run(sum_a_b, feed_dict=feed_dict)))
```

    1st Result: 7.0
    2nd Result: 4037.0

# MNIST Example

The MNIST dataset is very popular machine learning dataset, consisting of 70000 grayscale images of handwritten digits, of dimensions 28x28. We'll be using it as our example for this section of the tutorial, with the goal being to predict which the digit is in each image.

Since it's such a common (and small) dataset, TensorFlow has commands for downloading and formatting the dataset conveniently baked in already:

```python
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)
```

    Successfully downloaded train-images-idx3-ubyte.gz 9912422 bytes.
    Extracting MNIST_data/train-images-idx3-ubyte.gz
    Successfully downloaded train-labels-idx1-ubyte.gz 28881 bytes.
    Extracting MNIST_data/train-labels-idx1-ubyte.gz
    Successfully downloaded t10k-images-idx3-ubyte.gz 1648877 bytes.
    Extracting MNIST_data/t10k-images-idx3-ubyte.gz
    Successfully downloaded t10k-labels-idx1-ubyte.gz 4542 bytes.
    Extracting MNIST_data/t10k-labels-idx1-ubyte.gz

Let's take a look at how the data is organized:

```python
# Dataset statistics
print('Training image data: {0}'.format(mnist.train.images.shape))
print('Validation image data: {0}'.format(mnist.validation.images.shape))
print('Testing image data: {0}'.format(mnist.test.images.shape))
print('28 x 28 = {0}'.format(28*28))

print('\nTest Labels: {0}'.format(mnist.test.labels.shape))
labels = np.arange(10)
num_labels = np.sum(mnist.test.labels, axis=0, dtype=np.int)
print('Label distribution:{0}'.format(list(zip(labels, num_labels))))

# Example image
print('\nTrain image 1 is labelled one-hot as {0}'.format(mnist.train.labels[1,:]))
image = np.reshape(mnist.train.images[1,:],[28,28])
plt.imshow(image, cmap='gray')
```

### Logistic Regression Model

Define the graph input: this is where we feed in our training images into the model. Since MNIST digits are pretty small and the model we're using is very simple, we'll feed them in as flat vectors.

```python
# Define input placeholder
x = tf.placeholder(tf.float32, [None, 784])
```

To get our predicted probabilities of each digit, let's first start with the probability of a digit being a 3 like the image above. For our simple model, we start by applying a linear transformation. That is, we multiply each value of the input vector by a weight, sum them all together, and then add a bias. In equation form:

\begin{align}
y_3 = \sum_i w_{i,3} x_i + b_3
\end{align}

The magnitude of this result $y_3$, we'll take as being correlated to our belief in how likely we think the input digit was a 3. The higher the value of $y_3$, the more likely we think the input image $x$ was a 3 (ie, we'd hope we'd get a relatively large value for $y_3$ for the above image). Remember though, our original goal was to identify all 10 digits, so we also have:

\begin{align*}
y_0 =& \sum_i w_{i,0} x_i + b_0 \\
&\vdots \\
y_9 =& \sum_i w_{i,9} x_i + b_9
\end{align*}

We can express this in matrix form as:

\begin{align}
y = W x + b
\end{align}

To put this into our graph in TensorFlow, we need to define some Variables to hold the weights and biases:

```python
# Define linear transformation
W = tf.Variable(tf.zeros([784, 10]))
b = tf.Variable(tf.zeros([10]))
y = tf.matmul(x, W) + b
```

We can interpret these values (aka logits) $y$ as probabilities if we normalize them to be positive and add up to 1. In logistic regression, we do this with a softmax:

\begin{align}
p(y_i) = \text{softmax}(y_i) = \frac{\text{exp}(y_i)}{\sum_j\text{exp}(y_j)}
\end{align}

Notice that because the range of the exponential function is always non-negative, and since we're normalizing by the sum, the softmax achieves the desired property of producing values between 0 and 1 that sum to 1. If we look at the case with only 2 classes, we see that the softmax is the multi-class extension of the binary sigmoid function:

Computing a softmax in TensorFlow is pretty easy, sort of*:

**More on this later*

```python
# Softmax to probabilities
py = tf.nn.softmax(y)
```

That defines our forward pass of our model! We now have a graph that performs a forward pass: given an input image, the graph returns the probabilities the model thinks the input is each of the 10 classes. Are we done?

Not quite. We don't know the values of $W$ and $b$ yet. We're going to learn those by defining a loss and using gradient descent to do backpropagation. Essentially, we'll be taking the derivative with respect to each of the elements in $W$ and $b$ and wiggling them in a direction that reduces our loss.

The loss we commonly use in classification is cross-entropy. Cross-entropy is a concept from information theory:

\begin{align}
H_{y'}(y)=-\sum_i y'_i \text{log}(y_i)
\end{align}

Cross-entropy not only captures how *correct* (max probability corresponds to the right answer) the model's answers are, it also accounts for how *confident* (high confidence in correct answers) they are. This encourages the model to produce very high probabilities for correct answers while driving down the probabilities for the wrong answers, instead of merely be satisfied with it being the argmax.

In supervised models, we need labels to learn, so we create a placeholder for the labels in our training data:

```python
# Define labels placeholder
y_ = tf.placeholder(tf.float32, [None, 10])
```

The cross-entropy loss is pretty easy to implement:

```python
# Loss
cross_entropy = tf.reduce_mean(-tf.reduce_sum(y_ * tf.log(py), reduction_indices=[1]))
```

In the old days, we would have to go through and derive all the gradients ourselves, then code them into our program. Nowadays, we have libraries to compute all the gradients automatically. Not only that, but TensorFlow comes with a whole suite of optimizers implementing various optimization algorithms. I'm not going to go into the details of why you should appreciate that right now, because I know that Prof David Carlson has an entire day's worth of material on optimization.

```python
# Optimizer
train_step = tf.train.GradientDescentOptimizer(0.05).minimize(cross_entropy)
```

To train, we simply call the optimizer op we defined above. First though, we need to start a session and initialize our variables:

```python
# Create a session object and initialize all graph variables
sess = tf.Session()
sess.run(tf.global_variables_initializer())
```

There are much cleverer ways to design a training regimen that stop training once the model is converged and before it starts overfitting, but for this demo, we'll keep it simple:

```python
# Train the model
# trange is a tqdm function. It's the same as range, but adds a pretty progress bar
for _ in trange(1000):
    batch_xs, batch_ys = mnist.train.next_batch(100)
    sess.run(train_step, feed_dict={x: batch_xs, y_: batch_ys})
```

    100%|█████████████████████████████████████████████████████████████████████████████| 1000/1000 [00:01<00:00, 926.08it/s]

Notice, because of the way the dependency links are connected in our graph, running the optimizer requires an input to both the training image placeholder `x` and the training label placeholder `y_` (as it should). The values of all variables (`W` and `b`) are updated in place automatically by the optimizer.

Now let's see how we did! For every image in our test set, we run the data through the model, and take the digit in which we have the highest confidence as our answer. We then compute an accuracy by seeing how many we got correct:

```python
# Test trained model
correct_prediction = tf.equal(tf.argmax(py, 1), tf.argmax(y_, 1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
print('Test accuracy: {0}'.format(sess.run(accuracy, feed_dict={x: mnist.test.images, y_: mnist.test.labels})))
```

    Test accuracy: 0.9010999798774719

Not bad for a simple model and a few lines of code.  Before we close the session, there's one more interesting thing we can do. Normally, it can be difficult to inspect exactly what the filters in a model are doing, but since this model is so simple, and the weights transform the data directly to their logits, we can actually visualize what the model's learning by simply plotting the weights. The results look pretty reasonable:

```python
# Get weights
weights = sess.run(W)

fig, ax = plt.subplots(1, 10, figsize=(20, 2))

for digit in range(10):
    ax[digit].imshow(weights[:,digit].reshape(28,28), cmap='gray')

# Close session to finish
sess.close()
```

The entire model, with the complete model definition, training, and evaluation (but minus the weights visualization), is below. Note the slight difference when calculating the softmax; this is done for numerical stability purposes.

```python
import tensorflow as tf
from tqdm import trange
from tensorflow.examples.tutorials.mnist import input_data

# Import data
mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)

# Create the model
x = tf.placeholder(tf.float32, [None, 784])
W = tf.Variable(tf.zeros([784, 10]))
b = tf.Variable(tf.zeros([10]))
y = tf.matmul(x, W) + b

# Define loss and optimizer
y_ = tf.placeholder(tf.float32, [None, 10])
cross_entropy = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=y_, logits=y))
train_step = tf.train.GradientDescentOptimizer(0.5).minimize(cross_entropy)

# Create a Session object, initialize all variables
sess = tf.Session()
sess.run(tf.global_variables_initializer())

# Train
for _ in trange(1000):
    batch_xs, batch_ys = mnist.train.next_batch(100)
    sess.run(train_step, feed_dict={x: batch_xs, y_: batch_ys})

# Test trained model
correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
print('Test accuracy: {0}'.format(sess.run(accuracy, feed_dict={x: mnist.test.images, y_: mnist.test.labels})))

sess.close()
```

    Extracting MNIST_data/train-images-idx3-ubyte.gz
    Extracting MNIST_data/train-labels-idx1-ubyte.gz
    Extracting MNIST_data/t10k-images-idx3-ubyte.gz
    Extracting MNIST_data/t10k-labels-idx1-ubyte.gz

    100%|█████████████████████████████████████████████████████████████████████████████| 1000/1000 [00:01<00:00, 962.83it/s]

    Test accuracy: 0.9136000275611877

Note: The accuracy from the full version directly above might return a slightly different test accuracy from the step-by-step version we first went through. This is because mnist.train.next_batch by default shuffles the order of the training data, so we're seeing the data in a different order.

*Acknowledgment: Material adapted from the TensorFlow tutorial: https://www.tensorflow.org/get_started/*


```python
import math
import numpy as np
from sympy import *
t, s, k = symbols('t, s, k')
```

```python
# Defino los métodos de suma de rectángulos - izquierda
def _Riemman_izq(Func, limA, limB, numI):
    """ Método de la Bisección para encontrar raíces en un intervalo.

        ## Parámetros:
            Func (function) : función que depende de una variable.
            limA (function) : derivada de la función.
            limB (int)      : semilla de la solución.
            numI (int)      : número máximo de iteraciones.

        ## Devoluciones:
            Area (float)       : valor de area encontrado.
    """
    Δx = (limB - limA) / numI
    Alturas = 0
    for i in range(numI):
        Alturas += Func(limA + i * Δx)

    Area = Δx * Alturas
    return Area

# Defino los métodos de suma de rectángulos - izquierda
def _Riemman_der(Func, limA, limB, numI):
    """ Método de la Bisección para encontrar raíces en un intervalo.

        ## Parámetros:
            Func (function) : función que depende de una variable.
            limA (function) : derivada de la función.
            limB (int)      : semilla de la solución.
            numI (int)      : número máximo de iteraciones.

        ## Devoluciones:
            Area (float)       : valor de area encontrado.
    """
    Δx = (limB - limA) / numI
    Alturas = 0
    for i in range(numI):
        Alturas += Func(limB - i * Δx)

    Area = Δx * Alturas
    return Area

# Defino los métodos de suma de rectángulos - izquierda
def _Riemman_med(Func, limA, limB, numI):
    """ Método de la Bisección para encontrar raíces en un intervalo.

        ## Parámetros:
            Func (function) : función que depende de una variable.
            limA (function) : derivada de la función.
            limB (int)      : semilla de la solución.
            numI (int)      : número máximo de iteraciones.

        ## Devoluciones:
            Area (float)       : valor de area encontrado.
    """
    Δx = (limB - limA) / numI
    Alturas = 0
    for i in range(numI):
        Alturas += Func(limA + (2*i + 1)/2 * Δx)

    Area = Δx * Alturas
    return Area

# Defino el métodos del trapecio
def _trapecio(Func, limA, limB, numI):
    """ Método de la Bisección para encontrar raíces en un intervalo.

        ## Parámetros:
            Func (function) : función que depende de una variable.
            limA (function) : derivada de la función.
            limB (int)      : semilla de la solución.
            numI (int)      : número máximo de iteraciones.

        ## Devoluciones:
            Area (float)       : valor de area encontrado.
    """
    Δx = (limB - limA) / numI
    Bases = 0
    for i in range(numI):
        Bases += Func(limA + i * Δx) + Func(limA + Δx + i * Δx)

    Area = Δx/2 * Bases
    return Area

# Defino el métodos de la regla simpson
def _simpson(Func, limA, limB, numI):
    """ Método de la Bisección para encontrar raíces en un intervalo.

        ## Parámetros:
            Func (function) : función que depende de una variable.
            limA (function) : derivada de la función.
            limB (int)      : semilla de la solución.
            numI (int)      : número máximo de iteraciones.

        ## Devoluciones:
            Area (float)       : valor de area encontrado.
    """
    Δx = (limB - limA) / numI
    Bases = 0
    for i in range(numI):
        Bases += Func(limA + i * Δx) + 4 * Func(limA + (2*i + 1)/2 * Δx) + Func(limA + Δx + i * Δx)

    Area = Δx/6 * Bases
    return Area
```

```python
# 2
una_lista = [[1,2,3,[0,]], [4,5,6,0], [7,8,9,(0,1)]]
una_lista[1:][1][3][1]

```

    1

```python
# 3
A = [(1,  2),  (-3,  4)]
b = [14,  18]
M11 = 4
detA = np.linalg.det(A)
Zt = Integral(s, (s, t, M11))
funtion_Zt = Zt.doit()
Zt1 = detA * funtion_Zt.subs(t, 1)
display(Zt, funtion_Zt, Zt1)

# math.floor(Z11)
```

$\displaystyle \int\limits_{t}^{4} s\, ds$

$\displaystyle 8 - \frac{t^{2}}{2}$

$\displaystyle 75.0$

```python
# 4
A = [(1, 3), (0, 3)]
b = [14, 21]
x = np.matmul(np.linalg.inv(A), np.transpose(b))
x1 = x[0]
x2 = x[1]
ft = 2*t
Ft = Integral(ft, t)
Ft_ab = Integral(ft, (t, x2, x1))

# Evaluar la integral
Rt = Ft.doit()
Rt_ab = Ft_ab.doit()
display(Eq(Ft, Rt))
display(Ft_ab, Rt_ab)

```

$\displaystyle \int 2 t\, dt = t^{2}$

$\displaystyle \int\limits_{7.0}^{-7.0} 2 t\, dt$

$\displaystyle 0$

```python
# 5
F2 = lambda x: (x**2 + 3)**3
Va = _Riemman_der(F2, 4, 0, 7)
print(Va)
print(-3000 - Va)
print(-4000 - Va)
```

    -3150.264595777026
    150.264595777026
    -849.735404222974

```python
# 8
F4 = lambda k: math.e**(k)/k
va_1 = _trapecio(F4, 2, 4, 4)
va_2 = _simpson(F4, 2, 4, 4)
print(f"{va_1:7.5f}")
print(f"{va_2:7.5f}")

```

    14.85088
    14.67678

```python
# 9
F3 = lambda k: math.e**(2*k)
vr = Integral(math.e**(2*k), (k, 0, 3)).doit()
va = _Riemman_med(F3, 0, 3, 3)
er = 100 * abs(vr-va)/va
print(vr, va, er)
```

    201.214396746367 171.21697785422327 17.5201193643801