```python
from IPython.core.display import display_html
from urllib.request import urlopen

cssurl = 'http://j.mp/1DnuN9M'
display_html(urlopen(cssurl).read(), raw=True)
```

<link href='http://fonts.googleapis.com/css?family=EB+Garamond' rel='stylesheet' type='text/css'>
<link href='http://fonts.googleapis.com/css?family=Lato:100,100italic' rel='stylesheet' type='text/css'>

<style>
	@font-face{
   		font-family: "Computer Modern";
        	src: url('http://mirrors.ctan.org/fonts/cm-unicode/fonts/otf/cmunss.otf');}

        div.cell{
	        max-width:    800px;
	        margin-left:  auto;
	        margin-right: auto;}

	h1 {
        	font-family: 'Lato', sans-serif;}

	h2 {
        	font-family: 'Lato', serif;}

	h3{
		font-family:   'Lato', serif;
        	margin-top:    12px;
		margin-bottom: 3px;}

        h4{
		font-family: 'Lato', serif;}

	h5 {
        	font-family: 'Lato', serif;}

	div.text_cell_render{
        	font-family:  'EB Garamond', 'Computer Modern', 'Helvetica Neue', Arial, Helvetica, Geneva, sans-serif;
	        line-height:  140%;
	        font-size:    14pt;
		max-width:    650px;
		margin-left:  auto;
        	margin-right: auto;}

	.CodeMirror{
        	font-family: 'Consolas';}

	.text_cell_render h1 {
	        font-weight:   100;
	        font-size:     48pt;
		line-height:   100%;
	        color:         #CD2305;
	        margin-bottom: 0.5em;
	        margin-top:    0.5em;
	        display:       block;}

	.text_cell_render h2 {
        	font-weight:   100;
        	font-size:     36pt;}

	.text_cell_render h3 {
        	font-weight:   100;
        	font-size:     21pt;}

	.text_cell_render h4 {
        	font-weight:   100;
        	font-size:     18pt;}

	.text_cell_render h5 {
	        font-weight:   100;
	        font-size:     16pt;
	        color:         #CD2305;
	        font-style:    italic;
	        margin-bottom: 0.5em;
	        margin-top:    0.5em;
	        display:       block;}

	.text_cell_render h6 {
	        font-weight:   100;
	        font-size:     14pt;
	        color:         #CD2305;
	        font-style:    italic;
	        margin-bottom: 0.5em;
	        margin-top:    0.5em;
	        display:       block;}

	.warning{
	    	background-color: #FFFFE0;
	    	border-color: #FFCC99;
	    	border-left: 5px solid #FFCC99;
	    	padding: 0.5em;}

	.error{
	    	background-color: #fcf2f2;
	    	border-color: #dFb5b4;
	    	border-left: 5px solid #dfb5b4;
	    	padding: 0.5em;}

</style>

# Tarea 5 - Demostración de la formula de Euler

La formula de Euler es:

$$
e^{ix} = \cos{x} + i \sin{x}
$$

# Tarea 6 - Determinación de las $D$-particiones del espacio de parametros

Dado el sistema:

$$
\dot{x}(t) = a x(t) + b x(t - h)
$$

empezamos por obtener la transformada de Laplace del sistema, lo cual nos dará el siguiente cuasipolinomio caracteristico:

$$
p(s) = s - a - b e^{-h s} = 0
$$

Para determinar los puntos en que nuestro polinomio caracterisitico tiene polos en el eje imaginario, es decir las fronteras en que los parametros dejan de definir a un sistema estable  y comienzan a definir un sistema inestable, vamos a sustitur los valores $s = 0$ y $s = j \omega$, que son los valores que caracterizan al eje imaginario.

Empezamos sustituyendo $s = 0$, por lo que obtenemos:

$$
p(0) = -a - b = 0 \implies a = -b
$$

Si ahora sustituimos $s = j \omega$, tendremos:

$$
\begin{align}
p(j \omega) &= j \omega - a - b e^{- h j \omega} \\
&= j \omega - a - b \left( \cos{(\omega h)} -j \sin{(\omega h)} \right) \\
&= j \omega - a - b \cos{(\omega h)} + b j \sin{(\omega h)}
\end{align}
$$

de donde podemos separar la parte real de la imaginaria y obtener:

$$
\omega + b \sin{(\omega h)} = 0
$$

y

$$
-a -b \cos{(\omega h)} = 0
$$

Aqui podemos obtener una relación para $\sin{(\omega h)}$ y $\cos{(\omega h)}$:

$$
- \omega = b \sin{(\omega h)} \implies \cos{(\omega h)} = - \frac{a}{b}
$$

$$
\omega = - b \sin{(\omega h)} \implies \sin{(\omega  h)} = - \frac{\omega}{b}
$$

y sabemos que $\sin^2{(\omega h)} + \cos^2{(\omega h)} = 1$, por lo que podemos sustituir los valores que obtuvimos y tenemos que:

$$
\left( - \frac{\omega}{b} \right)^2 + \left( - \frac{a}{b} \right)^2 = 1 = \frac{\omega^2 + a^2}{b^2}
$$

despejando $\omega^2$ y sacando raiz cuadrada obtenemos:

$$
\omega^2 + a^2 = b^2
$$

$$
\omega^2 = b^2 - a^2
$$

$$
\omega = \sqrt{b^2 - a^2}
$$

al sustituir en la ecuación obtenida de la parte real del cuasipolinomio, obtenemos:

$$
-a -b \cos{(\sqrt{b^2 - a^2} h)} = 0
$$

o bien:

$$
a + b \cos{(\sqrt{b^2 - a^2} h)} = 0
$$

Esta es la relación entre $a$ y $b$ que nos dará las curvas de las $D$-particiones del espacio de parametros

# Tarea 7 - Gráfica de las $D$-particiones del espacio de parametros

Si bien las relaciones obtenidas son convenientes para su análisis, el graficarla por medio de software proporciona problemas, ya que sus variables no se pueden separar para obtener una en función de la otra, por lo que retrocederemos un poco y utilizaremos las relaciones obtenidas de separar las partes real e imaginaria del cuasipolinomio como ecuaciones parametricas para $a$ y $b$.

Empezamos obteniendo los valores para $a$ y $b$ en función de $\omega$:

$$
\omega + b \sin{(\omega h)} = 0 \implies b = - \frac{\omega}{\sin{(\omega h)}}
$$

$$
- a - b \cos{(\omega h)} = 0 \implies a = - b \cos{(\omega h)} = + \frac{\omega}{\sin{(\omega h)}} \cos{(\omega h)} = \frac{\omega}{\tan{(\omega h)}}
$$

Por lo que procedemos a capturar estas funciones en el programa, primero importamos las librerias que necesitamos para calcular y graficar:

```python
# Se importan librerias para graficar, y se define un estilo especifico
%matplotlib inline
from matplotlib.pyplot import plot, style, figure, legend
style.use("ggplot")
```

```python
# Se importan funciones de calculo numerico a utilizar
from numpy import linspace, tan, sin, pi
```

Ahora definimos las funciones que hemos obtenido:

```python
a  = lambda om, h: -om/sin(om*h)
b  = lambda om, h:  om/tan(om*h)
f1 = lambda     x: -x
```

Esta notación es equivalente a las definiciones matematicas:

$$
a(\omega, h) := - \frac{\omega}{\sin{(\omega h)}}
$$

$$
b(\omega, h) := \frac{\omega}{\tan{(\omega h)}}
$$

$$
f_1(x) := -x
$$

Ahora definimos valores para $\omega$ y $b$ para ingresar en estas funciones:

```python
tau = 2*pi
w = linspace(-3*tau, 3*tau, 1000)
bs = linspace(-15, 15, 100)
```

Lo que equivale a decir que variaremos $\omega$ en el intervalo $[-3 \tau, 3 \tau] = [-6 \pi, 6 \pi]$ y a $b$ en $[-15, 15]$.

Ahora graficamos $a$ contra $b$ con las funciones parametricas obtenidas y $x$ contra $f_1(x)$:

```python
f = figure(figsize = (10, 10))
p1, = plot(b(w, 1), a(w, 1), ".")
p2, = plot(bs, f1(bs), ".")

ax = f.gca()
ax.set_ylabel(r"$a(\omega)$", fontsize=20)
ax.set_xlabel(r"$b(\omega)$", fontsize=20)
ax.set_xlim(-15, 15)
ax.set_ylim(-15, 15)

legend([p1, p2], [r"$a + b \cos{(\sqrt{b^2 - a^2} h)} = 0$", r"$a + b = 0$"]);
```

# Tarea 8 - Teorema de la función implicita

Puedes acceder a este notebook a traves de la página

http://bit.ly/1xvpRgo

o escaneando el siguiente código:

```python
# Codigo para generar codigo :)
from qrcode import make
img = make("http://bit.ly/1xvpRgo")
img.save("codigos/codigo5678.jpg")
```

IPythonNotebooks/Sistemas con retardo en la entrada/Tareas 5, 6, 7, 8.ipynb


Link Github

Resúmen Teórico de Medidas Electrónicas 1

Incertidumbre

Liaño, Lucas

# Contenidos

- **Introducción**
- **Marco Teórico**
    - Conceptos Básicos Metrología
    - ¿Qué es la incertidumbre?
    - Modelo matemático de una medición ($Y$)
    - Evaluación incertidumbre Tipo A
    - Evaluación incertidumbre Tipo B
    - Incertidumbre Conjunta
    - Grado de Confianza
    - Caso de análisis: $u_{i}(x_{i}) \gg u_{j}(X_{i})$
    - Caso de análisis: $u_{i}(x_{i}) \ll u_{j}(X_{i})$
    - Correlación

- **Experimentación**
    - Caso General
    - Caso Incertidumbre tipo A dominante
    - Caso Incertidumbre tipo B dominante
    - Ejemplo Correlación
- **Bibliografía**
***

# Introducción

El objetivo del presente documento es de resumir, al mismo tiempo que simular, los contenidos teóricos correspondientes a la unidad N°1 de la materia medidas 1. Para ello, utilizaremos los recursos disponibles en el drive de la materia.

    Link: https://drive.google.com/folderview?id=1p1eVB4UoS0C-5gyienup-XiewKsTpcNc

***

# Marco Teórico

## Conceptos Básicos Metrología

La de medición de una magnitud física, atributo de un cuerpo mensurable, consiste en el proceso mediante el cual se da a conocer el valor de dicha magnitud. A lo largo de la historia se han desarrollado diversos modelos de medición, todos ellos consisten en la comparación de la magnitud contra un patrón.

A su vez, a medida que se fueron confeccionando mejores métodos de medición, se empezó a tener en consideración el error en la medida. Este error consiste en una indicación cuantitativa de la calidad del resultado. Valor que demuestra la confiabilidad del proceso.

Actualmente, definimos al **resultado de una medición** como al conjunto de valores de una magnitud, atribuidos a un mensurando. Se puede definir a partir de una función distribución densidad de probabilidad (también denomidada _pdf_, de la sígla inglesa _probability density function_). El resultado de una medición está caracterizado por la media de la muestra, la incertidumbre y el grado de confianza de la medida.

Denominaremos **incertidumbre de una medición** al parámetro asociado con el resultado de la medición que caracteríza la dispersión de los valores atribuidos a un mensurando. Mientras que el **error de medida** será la diferencia entre el valor medido con un valor de referencia. [[1]](http://depa.fquim.unam.mx/amyd/archivero/CALCULODEINCERTIDUMBRESDR.JAVIERMIRANDA_26197.pdf)

#### Tipos de errores

Existen dos tipos:

> **Error sistemático:** Componente del error que en repetidas mediciones permanece constante.

> **Error aleatorio:** Componente del error que en repetidas mediciones varía de manera impredecible.

***
## ¿Qué es la incertidumbre?

Como bien definimos anteriormente, la incertidumbre es un parámetro que caracteríza la dispersión de los valores atribuidos a un mensurando. Esto significa que, considerando al resultado de la medición como una función distribución densidad de probabilidad, la incertidumbre representa el desvío estándar de la misma. Se suele denominar **incertidumbre estándar** a dicha expresión de la incertidumbre.

#### Componentes de la incertidumbre

> **Tipo A:** Componente de la incertidumbre descripta únicamente a partir del estudio estadístico de las muestras.

> **Tipo B:** Componente de la incertidumbre descripta a partir de las hojas de datos previstas por los fabricantes de los instrumentos de medición, junto con datos de calibración.

En las próximas secciones se describe en detalle como son los test efectuados para determinar cada una de las componentes. [[2]](https://es.wikipedia.org/wiki/Propagaci%C3%B3n_de_errores)

***
## Modelo matemático de una medición ($Y$)

Supongamos una magnitud a mensurar ($Y$), la cual se va a estimar de forma indirecta a partir de una relación fundamental con otras $N$ magnitudes mensurables, de manera que se cumple:

\begin{equation}
    Y = f(x_{1},x_{2},...,x_{N})
\end{equation}

Como definimos previamente, las variables $x_{i}$ son funciones distribución densidad de probabilidad por ser resultados de mediciones. Cada una de estas mediciones viene determinada, idealmente, por el valor de su media ($\mu_{X_{i}}$), su desvío estándar ($\sigma_{x_{i}}$) y el grado de confianza de la medición. Dado que en la vida real no es posible conseguir una estimación lo suficientemente buena de estos parámetros, se utilizarán sus estimadores en su lugar.

Por tanto, si se tomaron $M$ muestras de cada una de estas variables, podemos utilizar la **media poblacional ($\bar{Y}$)** como estimador de la media ($\mu_{Y}$) de la distribución densidad de probabilidad de la medición como:

\begin{equation}
     \hat{Y} = \bar{Y} = \frac{1}{M} \sum_{k=0}^{M} f_{k}(x_{1},x_{2},...,x_{N}) = f(\bar{X_{1}},\bar{X_{2}},...,\bar{X_{N}})
\end{equation}

    Verificar que esto este bien. Sospecho que no porque estamos suponiendo que podes aplicar linealidad adentro de la función. Estoy leyendo el ejemplo del calculo de resistencia y hacemos "resistencia= (media_V/media_I)" en la línea 39 del documento compartido en el canal general de Slack.

Asimismo, para determinar el otro parámetro fundamental de la medición (la incertidumbre) utilizaremos como estimador a la **incertidumbre combinada ($u_{c}$)** definida a partir de la siguiente ecuación,

\begin{equation}
     u_{c}^{2}(Y) = \sum_{i=1}^{N} (\dfrac{\partial f}{\partial x_{i}})^{2} \cdot u_{c}^{2}(x_{i}) + 2 \sum_{i=1}^{N-1} \sum_{j = i+1}^{N} \dfrac{\partial f}{\partial x_{i}} \dfrac{\partial f}{\partial x_{j}} u(x_{i},x_{j})
\end{equation}

donde $u(x_{i},x_{j})$ es la expresión de la covariancia entre las pdf de las $x_{i}$.

Esta expresión, para permitir el uso de funciones $f_{k}$ no lineales, es la aproximación por serie de Taylor de primer orden de la expresión original para funciones que cumplen linealidad. [[2]](https://es.wikipedia.org/wiki/Propagaci%C3%B3n_de_errores)

A su vez, a partir de la **ley de propagación de incertidumbres**, podemos decir que para la determinación de una variable unitaria mediante medición directa es posible reducir la expresión anterior a la siguiente:

\begin{equation}
     u_{c}^{2}(x_{i}) = u_{i}^{2}(x_{i}) + u_{j}^{2}(x_{i})
\end{equation}

donde denominaremos $u_{i}(x_{i})$ a la incertidumbre tipo A, y $u_{j}(x_{i})$ a la incertidumbre tipo B.

***
## Evaluación incertidumbre Tipo A

La incertidumbre tipo A, recordando que se trata de una medida de dispersión y al ser tipo A se relaciona con la estadística de las muestras, se puede estimar con el desvío estándar experimental de la media ($S(\bar{X_{i}})$). Para ello hace falta recordar algunos conceptos de estadística.

Suponiendo que se toman $N$ muestras:

> **Estimador media poblacional:**
>> $\hat{x_{i}}=\bar{X_{i}}=\dfrac{1}{N} \sum_{k=1}^{N}x_{i,k}$

> **Grados de libertad:**
>> $\nu = N-1$

> **Varianza experimental de las observaciones:**
>> $\hat{\sigma^{2}(X_{i})}=S^{2}(X_{i})=\dfrac{1}{\nu} \sum_{k=1}^{N}(X_{i,k} - \bar{X_{i}})^{2}$

> **Varianza experimental de la media:**
>> $\hat{\sigma^{2}(\bar{X_{i}})}=S^{2}(\bar{X_{i}})=\dfrac{S^{2}(x_{i})}{N}$

    Por ende, la componente de la incertidumbre tipo A nos queda:

\begin{equation}
     u_{i}(x_{i}) = \sqrt{S^{2}(\bar{X_{i}})}
\end{equation}

    Nota: Para calcular el std con un divisor de $\nu = N-1$ es necesario modificar un argumento en la función de python. El comando correctamente utilizado es: 'myVars.std(ddof=1)'.

***
## Evaluación incertidumbre Tipo B

La incertidumbre tipo B viene determinada por la información que proveen los fabricantes de los instrumentos de medición, asi como también por los datos resultantes por la calibración de los mismos.

En estos instrumentos de medición la incertidumbre viene descripta en forma de distribuciones densidad de probabilidad, no en forma estadística. Para ello utilizamos los siguientes estadísticos que caracterízan a las variables aleatorias, en caso de que su dominio fuera continuo:

> **Esperanza:**
>> $E(x)=\int x.f(x)dx$

> **Varianza:**
>> $V(x)=\int x^{2}.f(x)dx$

    Por tanto, si la incertidumbre es un parámetro de dispersión, la misma vendrá descripta por la expresión:

\begin{equation}
     u_{j}(x_{i}) = \sqrt{V(x)}
\end{equation}

Por simplicidad a la hora de trabajar, a continuación se presenta una tabla con los valores típicos del desvío estándar para el caso de distintas distribuciones. Se demuestra el caso de distribución uniforme.

Suponiendo que la distribución esta centrada en $\bar{X_{i}}$, nos quedaría que $a = \bar{X_{i}} - \Delta X$ y $b = \bar{X_{i}} - \Delta X$.

Por tanto si la expresión de la varianza es $V(x_{i}) = \frac{(b-a)^{2}}{12}$, finalmente quedaría:

\begin{equation}
     V(x_{i}) = \frac{(b-a)^{2}}{12} = \frac{(2 \Delta X)^{2}}{12} = \frac{4 \Delta X^{2}}{12} = \frac{\Delta X^{2}}{3}
\end{equation}

\begin{equation}
     \sigma_{x_{i}} = \frac{\Delta X}{\sqrt{3}}
\end{equation}

Finalmente la tabla queda,

| Distribution     | $u_{j}(x_{i}) = \sigma_{x_{i}}$|
|    :----:        |    :----:                   |
|  Uniforme        | $\frac{\Delta X}{\sqrt{3}}$ |
|  Normal          | $\Delta X                 $ |
|  Normal ($K=2$)  | $\frac{\Delta X}{2}       $ |
|  Triangular      | $\frac{\Delta X}{\sqrt{6}}$ |
|  U               | $\frac{\Delta X}{\sqrt{2}}$ |

    Verificar que esto este bien. Me genera dudas el término $\Delta X$. Esto no creo que deba ser así porque en el caso de la distribución normal $\sigma_{x_{i}} = \sigma$. No creo que deba aparecer ningun error absoluto ahí.

***
## Incertidumbre Conjunta

Como definimos anteriormente, la incertidumbre conjunta queda definida como:

\begin{equation}
     u_{c}^{2}(x_{i}) = u_{i}^{2}(x_{i}) + u_{j}^{2}(x_{i})
\end{equation}

#### ¿Qué función distribución densidad de probabilidad tiene $u_{c}$?

Si se conocen $x_{1},x_{2},...,x_{N}$ y $Y$ es una combinación lineal de $x_{i}$ (o en su defecto una aproximación lineal, como en el caso del polinomio de taylor de primer grado de la función), podemos conocer la función distribución densidad de probabilidad a partir de la convolución de las $x_{i}$, al igual que se hace para SLIT. [[3]](https://es.wikipedia.org/wiki/Convoluci%C3%B3n)

Dado que habitualmente no se conoce con precisión la función distribución densidad de probabilidad de $u_{i}(x_{i})$, se suele utilizar el **teorema central del límite** para conocer $u_{c}(x_{i})$. El mismo plantea que cuantas más funciones $x_{i}$ con función distribución densidad de probabilidad deconocida sumemos, más va a tender su resultado a una distribución normal.

***
## Grado de Confianza

Finalmente, el último parámetro que nos interesa conocer para determinar el resultado de la medición es el grado de confianza.

> **Grado de confianza:** Es la probabilidad de que al evaluar nuevamente la media poblacional ($\bar{y}$) nos encontremos con un valor dentro del intervalo $[\bar{Y} - K.\sigma_{Y}(\bar{Y}) \le \mu_{Y} \le \bar{Y} - K.\sigma_{Y}(\bar{Y})]$ para el caso de una distribución que cumpla el teorema central del límite, donde $K$ es el factor de cobertura.

Otra forma de verlo es:

donde el grado de confianza viene representado por $(1-\alpha)$. Recomiendo ver el ejemplo [[4]](https://es.wikipedia.org/wiki/Intervalo_de_confianza#Ejemplo_pr%C3%A1ctico) en caso de no entender lo que representa.

De esta forma, el factor de cobertura ($K$) nos permite modificar el grado de confianza. Agrandar $K$ aumentará el área bajo la curva de la gaussiana, lo que representará un mayor grado de confianza.

Se definirá **incertidumbre expandida** a $U(x_{i}) = K \cdot u_{c}(x_{i})$ si $u_{c}(x_{i})$ es la incertidumbre que nos proveé un grado de confianza de aproximadamente $ 68\% $.

Para una función que distribuye como normal podemos estimar el grado de confianza mediante la siguiente tabla,

| Factor de cobertura     | Grado de confianza|
|    :----:        |    :----: |
|  $K=1$           | $68.26\% $ |
|  $K=2$           | $95.44\% $ |
|  $K=3$           | $99.74\% $ |

#### ¿Qué sucede si $u_{c}$ no distribuye normalmente?

En este caso también se podrá utilizar la ecuación $U(x_{i}) = K \cdot u_{c}(x_{i})$, pero el método mediante el cual obtendremos a $K$ será distinto.

***
## Caso de análisis: $u_{i}(x_{i}) \gg u_{j}(X_{i})$

Cuando sucede que la incertidumbre que proveé la evaluación tipo A es muy significativa frente a la tipo B, esto querrá decir que no tenemos suficientes grados de libertad para que $u_{c}(x_{i})$ se aproxime a una gaussiana. En otras palabras, la muestra obtenida no es significativa.

En estos casos vamos a suponer que $u_{c}(x_{i})$ distribuye como t-Student. La distribución t-Student surge justamente del problema de estimar la media de una población normalmente distribuida cuando el tamaño de la muestra es pequeño.

Como la distribución de t-Student tiene como parámetro los grados de libertad efectivos, debemos calcularlos. Para ello utilizaremos la fórmula de Welch-Satterthwaite:

\begin{equation}
     \nu_{eff} = \dfrac{u_{c}^{4}(y)}{\sum_{i=1}^{N} \dfrac{ c_{i}^{4} u^{4}(x_{i})} {\nu_{i}} }
\end{equation}

donde $c_i = \dfrac{\partial f}{\partial x_{i}}$ y $u_{i}(x_{i})$ es la incertidumbre tipo A.

Para obtener el factor de cobertura que nos asegure un factor de cobertura del $95/%$ debemos recurrir a la tabla del t-Student. Para ello existe una función dentro del módulo _scipy.stats_ que nos integra la función hasta lograr un área del $95.4%$.

A continuación presentamos la función que utilizaremos con dicho fin,

~~~
def get_factor_Tstudent(V_eff, porcentaje_confianza_objetivo=95.4):
 """
 Funcion de calculo de factor de expansión por T-student
 input:
 V_eff: Grados de libertad (float)
 porcentaje_confianza_objetivo: porcentaje_confianza_objetivo (float)
 returns:
 Factor de expansión (float)
 """
 return np.abs( -(stats.t.ppf((1.0+(porcentaje_confianza_objetivo/100))/2.0,V_eff)) )
~~~

***
## Caso de análisis: $u_{i}(x_{i}) \ll u_{j}(X_{i})~$

Para el caso en el que la incertidumbre del muestreo es muy inferior a la incertidumbre tipo B, nos encontramos frente al caso de incertidumbre B dominante. Esta situación es equivalente a tener la convolución entre una delta de dirac con una función de distribución cualquiera.

Como observamos en la imagen, la función distribución densidad de probabilidad resultate se asemeja más a la distribución uniforme del tipo B. En este caso para encontrar el factor de cobertura utilizaremos otra tabla distinta. En esta tabla el parámetro de entrada es el cociente $\dfrac{u_{i}}{u_{j}}$.

A continuación presentamos la función que utilizaremos con dicho fin,

~~~
def tabla_B(arg):
  tabla_tipoB = np.array([
                 [0.0, 1.65],
                 [0.1, 1.66],
                 [0.15, 1.68],
                 [0.20, 1.70],
                 [0.25, 1.72],
                 [0.30, 1.75],
                 [0.35, 1.77],
                 [0.40, 1.79],
                 [0.45, 1.82],
                 [0.50, 1.84],
                 [0.55, 1.85],
                 [0.60, 1.87],
                 [0.65, 1.89],
                 [0.70, 1.90],
                 [0.75, 1.91],
                 [0.80, 1.92],
                 [0.85, 1.93],
                 [0.90, 1.94],
                 [0.95, 1.95],
                 [1.00, 1.95],
                 [1.10, 1.96],
                 [1.20, 1.97],
                 [1.40, 1.98],
                 [1.80, 1.99],
                 [1.90, 1.99]])
  if arg >= 2.0:
    K = 2.0
  else:
    pos_min = np.argmin(np.abs(tabla_tipoB[:,0]-arg))
    K =  tabla_tipoB[pos_min,1]

  return K
~~~

***
## Correlación

Finalmente nos encontramos con el caso mas general. En esta situación las variables se encuentran correlacionadas, por lo que la expresión de $u_{c}(Y)$ debe utilizarse en su totalidad.

Por simplicidad de computo vamos a definir al coeficiente correlación como,

\begin{equation}
     r(q,w) = \dfrac{ u(q,w) }{ u(q)u(w) }
\end{equation}

De esta forma podemos expresar a $u_{c}$ como:

\begin{equation}
     u_{c}^{2}(Y) = \sum_{i=1}^{N} (\dfrac{\partial f}{\partial x_{i}})^{2} \cdot u_{c}^{2}(x_{i}) + 2 \sum_{i=1}^{N-1} \sum_{j = i+1}^{N} \dfrac{\partial f}{\partial x_{i}} \dfrac{\partial f}{\partial x_{j}} r(x_{i},x_{j})u(x_{i})u(x_{j})
\end{equation}

Esta expresión debe utilizarse cada vez que $r(x_{i},x_{j}) \ne 0$.

# Experimentación
**Comenzamos inicializando los módulos necesarios**

```python
# módulos genericos
import numpy as np
import matplotlib.pyplot as plt
from scipy import stats
from scipy import signal

# Módulos para Jupyter (mejores graficos!)
import warnings
warnings.filterwarnings('ignore')
plt.rcParams['figure.figsize'] = [12, 4]
plt.rcParams['figure.dpi'] = 150 # 200 e.g. is really fine, but slower

from pandas import DataFrame
from IPython.display import HTML
```

**Definimos las funciones previamente mencionadas**

```python
Ahora# Tabla para el caso A dominante
def get_factor_Tstudent(V_eff, porcentaje_confianza_objetivo=95.4):
    """
    Funcion de calculo de factor de expansión por T-student
    input:
    V_eff: Grados de libertad (float)
    porcentaje_confianza_objetivo: porcentaje_confianza_objetivo (float)
    returns: .libertad efectivosdenoted
    Factor de expansión (float)
    """
    return np.abs( -(stats.t.ppf((1.0+(porcentaje_confianza_objetivo/100))/2.0,V_eff)) )

# Tabla para el caso B dominante
def tabla_B(arg):
    tabla_tipoB = np.array([
                 [0.0, 1.65],
                 [0.1, 1.66],
                 [0.15, 1.68],
                 [0.20, 1.70],
                 [0.25, 1.72],
                 [0.30, 1.75],
                 [0.35, 1.77],
                 [0.40, 1.79],
                 [0.45, 1.82],
                 [0.50, 1.84],
                 [0.55, 1.85],
                 [0.60, 1.87],
                 [0.65, 1.89],
                 [0.70, 1.90],
                 [0.75, 1.91],
                 [0.80, 1.92],
                 [0.85, 1.93],
                 [0.90, 1.94],
                 [0.95, 1.95],
                 [1.00, 1.95],
                 [1.10, 1.96],
                 [1.20, 1.97],
                 [1.40, 1.98],
                 [1.80, 1.99],
                 [1.90, 1.99]])
    if arg >= 2.0:
        K = 2.0
    else:
        pos_min = np.argmin(np.abs(tabla_tipoB[:,0]-arg))
        K =  tabla_tipoB[pos_min,1]

    return K
```

## Caso general
**Definimos las constantes necesarias**

```python
# Constantes del instrumento
CONST_ERROR_PORCENTUAL = 0.5 # Error porcentual del instrumento de medición
CONST_ERROR_CUENTA = 3       # Error en cuentas del instrumento de medición
CONST_DECIMALES = 2          # Cantidad de decimales que representa el instrumento

# Constantes del muestro
N = 10                       # Cantidad de muestras tomadas

# Señal a muestrear idealizada
mu = 100                     # Valor medio de la distribución normal de la población ideal
std = 2                      # Desvío estándar de la distribución normal de la población ideal

# Muestreo mi señal ideal (Normal)
muestra = np.random.randn(N) * std + mu
```

**Ahora solamente genero un gráfico que compare el histograma con la distribución normal de fondo**

```python
num_bins = 50
fig, ax = plt.subplots()
# the histogram of the 1.1data
n, bins, patches = ax.hist(muestra, num_bins, density=True)
# add a 'best fit' line
y = ((1 / (np.sqrt(2 * np.pi) * std)) *
     np.exp(-0.5 * (1 / std * (bins - mu))**2))
ax.plot(bins, y, '--')
ax.set_xlabel('Smarts')
ax.set_ylabel('Probability density')
ax.set_title('Histogram of IQ: $\mu=$'+ str(mu) + ', $\sigma=$' + str(std))
# Tweak spacing to prevent clipping of ylabel
fig.tight_layout()
plt.show()
```

```python
media = np.round(muestra.mean(), CONST_DECIMALES) # Redondeamos los decimales a los valores que puede ver el tester
desvio = muestra.std(ddof=1)

print("Mean:",media )
print("STD:" ,desvio)
```

    Mean: 99.29
    STD: 1.6777655348895033

**Calculamos el desvío estándar experimental de la media como:**
\begin{equation}
     u_{i}(x_{i}) = \sqrt{S^{2}(\bar{X_{i}})}
\end{equation}

```python
#Incertidumbre Tipo A
ui = desvio/np.sqrt(N)
ui
```

    0.5305560469981527

**Calculamos el error porcentual total del dispositivo de medición como:**
\begin{equation}
     e_{\%T} = e_{\%} + \dfrac{e_{cuenta}\cdot 100\%}{\bar{X_{i}}(10^{cte_{Decimales}})}
\end{equation}

```python
#Incertidumbre Tipo B
ERROR_PORCENTUAL_CUENTA = (CONST_ERROR_CUENTA*100)/(media * (10**CONST_DECIMALES ))

ERROR_PORCENTUAL_TOTAL = CONST_ERROR_PORCENTUAL + ERROR_PORCENTUAL_CUENTA

ERROR_PORCENTUAL_CUENTA
```

    0.030214523114110183

**Por tanto el error absoluto se representa como:**
\begin{equation}
     \Delta X = e_{\%T} \dfrac{\bar{X_{i}}}{100\%}
\end{equation}

```python
deltaX = ERROR_PORCENTUAL_TOTAL * media/100
deltaX
```

    0.5264500000000001

**Finalmente la incertidumbre tipo B queda:**
\begin{equation}
     u_{j}(x_{i}) = \sqrt{Var(x_{i})} = \dfrac{\Delta X}{\sqrt{3}}
\end{equation}

donde recordamos que, al suponer una distribución uniforme en el dispositivo de medición, la varianza nos queda $Var(X_{uniforme}) = \dfrac {(b-a)^{2}}{12}$.

```python
uj = deltaX / np.sqrt(3)
uj
```

    0.30394604921487856

**Calculamos la incertidumbre conjunta**

Como este es el caso de una medición directa de una sola variable, la expresión apropiada es:

\begin{equation}
     u_{c}^{2}(x_{i}) = u_{i}^{2}(x_{i}) + u_{j}^{2}(x_{i})
\end{equation}

```python
#incertidumbre combinada
uc = np.sqrt(ui**2 + uj**2)
uc
```

    0.61145148608834

**Ahora debemos evaluar frente a que caso nos encontramos**

En primera instancia evaluamos que componente de la incertidumbre es mayoritaria y en que proporción.

Entonces tenemos tres situaciones posibles:

1. **Caso B dominante** $\Rightarrow \dfrac{u_{i}(x_{i})}{u_{j}(x_{i})} \lt 1 \Rightarrow$  Se utiliza la tabla de B dominante.
1. **Caso Normal** $\Rightarrow \dfrac{u_{i}(x_{i})}{u_{j}(x_{i})} \gt 1$ y $V_{eff} \gt 30 \Rightarrow$  Se toma $K=2$.
1. **Caso A dominante** $\Rightarrow \dfrac{u_{i}(x_{i})}{u_{j}(x_{i})} \gt 1$ y $V_{eff} \lt 30 \Rightarrow$  Se utiliza t-Student con los grados de libertad efectivos.

```python
def evaluacion(uc,ui,uj,N):
    cte_prop = ui/uj
    print("Constante de proporcionalidad", cte_prop)
    if cte_prop > 1:
        # Calculo los grados de libertad efectivos
        veff = int ((uc**4)/((ui**4)/(N-1)))
        print("Grados efectivos: ", veff)
        if veff > 30:
            # Caso Normal
            k = 2
        else:
            # Caso t-Student
            k = get_factor_Tstudent(veff)
    else:
        # Caso B Dominante
        k = tabla_B(cte_prop)
    print("Constante de expansión: ",k)
    return k
```

    Nota: La contribución de $u_{j}(x_{i})$ no se tiene en cuenta dado que, al ser una distribución continua, tiene infinitos grados de libertad.

\begin{equation}
     \nu_{eff} = \dfrac{u_{c}^{4}(y)}{\sum_{i=1}^{N} \dfrac{ c_{i}^{4} u^{4}(x_{i})} {\nu_{i}} }
\end{equation}

```python
k = evaluacion(uc,ui,uj,N)
```

    Constante de proporcionalidad 1.7455599385766958
    Grados efectivos:  15
    Constante de expansión:  2.175422110927068

**Análisis y presentación del resultado**

Como el cociente $\dfrac{u_{i}(x_{i})}{u_{j}(x_{i})} \gt 2$, entonces suponemos que nos encontramos frente al caso de distribución normal o distribución t-Student. Para ello utilizamos el criterio de los grados de libertad efectivos.

En este caso los grado de libertad efectivos $V_{eff} \gt 30$, por lo que suponemos distribución normal.

Finalmente presentamos el resultado con 1 dígito significativo.

```python
U = uc*k
print("Resultado de la medición: (",np.round(media,1),"+-",np.round(U,1),")V con un grado de confianza del 95%")
```

    Resultado de la medición: ( 99.3 +- 1.3 )V con un grado de confianza del 95%

# Bibliografía

_Nota: Las citas **no** respetan el formato APA._

1. [Evaluación de la Incertidumbre en Datos Experimentales, Javier Miranda Martín del Campo](http://depa.fquim.unam.mx/amyd/archivero/CALCULODEINCERTIDUMBRESDR.JAVIERMIRANDA_26197.pdf)

1. [Propagación de erroes, Wikipedia](https://es.wikipedia.org/wiki/Propagaci%C3%B3n_de_errores)

1. [Convolución, Wikipedia](https://es.wikipedia.org/wiki/Convoluci%C3%B3n)

1. [Intervalo de Confianza, Wikipedia](https://es.wikipedia.org/wiki/Intervalo_de_confianza#Ejemplo_pr%C3%A1ctico)


# Python for scientific computing

> Marcos Duarte
> Laboratory of Biomechanics and Motor Control [http://demotu.org](http://demotu.org)
> Federal University of ABC, Brazil

# This talk

*The Python programming language with its ecosystem for scientific programming has features, maturity, and a community of developers and users that makes it the ideal environment for the scientific community.*

*This talk will show some of these features and usage examples.*

*If you are viewing this notebook online (served by [http://nbviewer.ipython.org](http://nbviewer.ipython.org)), you can click  the button 'View as Slides' on the toolbar above to start the slide show.*

## The lifecycle of a scientific idea

```python
from IPython.display import Image
Image(filename='../images/lifecycle_FPerez.png')  # From F. Perez
```

## About Python

*Python is a programming language that lets you work more quickly and integrate your systems more effectively. You can learn to use Python and see almost immediate gains in productivity and lower maintenance costs* [[python.org](http://python.org/)].

*Python is an interpreted, object-oriented, high-level programming language with dynamic semantics. Its high-level built in data structures, combined with dynamic typing and dynamic binding, well suited for Rapid Application Development and for scripting or glue language to connect existing components. Python's simple, easy to learn syntax emphasizes readability and therefore reduces the cost of program maintenance. Python supports modules and packages, which encourages program modularity and code reuse. The Python interpreter and standard libraries are available without charge for all major platforms, and can be freely distributed* [[Python documentation](http://www.python.org/doc/essays/blurb/)].

## About me

As a scientist, what I do it's similar to this other fellow:

```python
from IPython.display import YouTubeVideo
YouTubeVideo('9ZlBUglE6Hc', width=480, height=360, rel=0)
```

## Python ecosystem for scientific computing (main libraries)

- [Numpy](http://numpy.scipy.org): fundamental package for scientific computing with a N-dimensional array package.
- [Scipy](http://scipy.org/scipylib/index.html): numerical routines for scientific computing.
- [Matplotlib](http://matplotlib.org): comprehensive 2D Plotting.
- [Sympy](http://sympy.org): symbolic mathematics.
- [Pandas](http://pandas.pydata.org/): data structures and data analysis tools.
- [Jupyter Notebook](https://jupyter.org): web application for creating and sharing documents with live code, equations, visualizations and text.
- [Statsmodels](http://statsmodels.sourceforge.net/): to explore data, estimate statistical models, and perform statistical tests.
- [Scikit-learn](http://scikit-learn.org/stable/): tools for data mining and data analysis (including machine learning).
- [Pillow](http://python-pillow.github.io/): Python Imaging Library.
- [Spyder](https://code.google.com/p/spyderlib/): interactive development environment.

## Why Python and not 'X' (put any other language here)

Python is not the best programming language for all needs and for all people. There is no such language. But, if you are doing scientific computing, chances are that Python is perfect for you because:

1. Python is free, open source, and cross-platform.
2. Python is easy to learn, with readable code, well documented, and with a huge and friendly user community.
3. Python is a real programming language, able to handle a variety of problems, easy to scale from small to huge problems, and easy to integrate with other systems (including other programming languages).
4. Python code is not the fastest but Python is one the fastest languages for programming. It is not uncommon in science to care more about the time we spend programming than the time the program took to run. But if code speed is important, one can easily integrate in different ways a code written in other languages (such as C and Fortran) with Python.
5. The Jupyter Notebook is a versatile tool for programming, data visualization, plotting, simulation, numeric and symbolic mathematics, and writing for daily use.

## Popularity of Python for teaching

```python
from IPython.display import IFrame
IFrame('http://cacm.acm.org/blogs/blog-cacm/176450-python-is-now-the-most-popular-' +
       'introductory-teaching-language-at-top-us-universities/fulltext',
       width='100%', height=450)
```

## The Jupyter Notebook

The Jupyter Notebook App is a server-client application that allows editing and running notebook documents via a web browser. The Jupyter Notebook App can be executed on a local desktop requiring no internet access (as described in this document) or installed on a remote server and accessed through the internet.

Notebook documents (or “notebooks”, all lower case) are documents produced by the Jupyter Notebook App which contain both computer code (e.g. python) and rich text elements (paragraph, equations, figures, links, etc...). Notebook documents are both human-readable documents containing the analysis description and the results (figures, tables, etc..) as well as executable documents which can be run to perform data analysis.

[Try Jupyter Notebook in your browser](https://try.jupyter.org/).

```python
from IPython.display import IFrame
IFrame('https://jupyter.org/', width='100%', height=450)
```

## Jupyter Notebook and IPython kernel architectures

## Python installation and tutorial

- [Python for scientific computing](http://nbviewer.ipython.org/github/demotu/BMC/blob/master/notebooks/PythonForScientificComputing.ipynb)
- [How to install Python for scientific computing](http://nbviewer.ipython.org/github/demotu/BMC/blob/master/notebooks/PythonInstallation.ipynb)
- [Tutorial on Python for scientific computing](http://nbviewer.ipython.org/github/demotu/BMC/blob/master/notebooks/PythonTutorial.ipynb)

## Installing the Python ecosystem

**The easy way**
The easiest way to get Python and the most popular packages for scientific programming is to install them with a Python distribution such as [Anaconda](https://www.continuum.io/anaconda-overview). In fact, you don't even need to install Python in your computer, you can run Python for scientific programming in the cloud using [python.org](https://www.python.org/shell/), [SageMathCloud](https://cloud.sagemath.com), [Wakari](https://www.wakari.io/), [pythonanywhere](https://www.pythonanywhere.com/), or [repl.it](https://repl.it/languages/python3).

**The hard way**
You can download Python and all individual packages you need and install them one by one. In general, it's not that difficult, but it can become challenging and painful for certain big packages heavily dependent on math, image visualization, and your operating system (i.e., Microsoft Windows).

## Anaconda

Go to the [*Anaconda* website](https://www.continuum.io/downloads) and download the appropriate version for your computer (but download Anaconda3! for Python 3.x). The file is big (about 350 MB). [From their website](https://www.continuum.io/downloads):
**Linux Install**
In your terminal window type and follow the instructions:
```
bash Anaconda3-4.1.1-Linux-x86_64.sh
```
**OS X Install**
For the graphical installer, double-click the downloaded .pkg file and follow the instructions
For the command-line installer, in your terminal window type and follow the instructions:
```
bash Anaconda3-4.1.1-MacOSX-x86_64.sh
```
**Windows**
Double-click the .exe file to install Anaconda and follow the instructions on the screen

## Miniconda

A variation of *Anaconda* is [*Miniconda*](http://conda.pydata.org/miniconda.html) (Miniconda3 for Python 3.x), which contains only the *Conda* package manager and Python.

Once *Miniconda* is installed, you can use the `conda` command to install any other packages and create environments, etc.

# My current installation

```python
# pip install version_information
%load_ext version_information
%version_information numpy, scipy, matplotlib, sympy, pandas, ipython, jupyter
```

Software	Version

Python	3.5.2 64bit [MSC v.1900 64 bit (AMD64)]

IPython	5.1.0

OS	Windows 10 10.0.10586 SP0

numpy	1.11.1

scipy	0.18.0

matplotlib	1.5.3

sympy	1.0

pandas	0.18.1

ipython	5.1.0

jupyter	1.0.0

Thu Sep 22 01:04:43 2016 E. South America Standard Time

## To learn more about Python

There is a lot of good material in the internet about Python for scientific computing, some of them are:

 - [How To Think Like A Computer Scientist](http://www.openbookproject.net/thinkcs/python/english2e/) or [the interactive edition](http://interactivepython.org/courselib/static/thinkcspy/index.html) (book)
 - [Python Scientific Lecture Notes](http://scipy-lectures.github.io/) (lecture notes)
 - [Lectures on scientific computing with Python](https://github.com/jrjohansson/scientific-python-lectures#lectures-on-scientific-computing-with-python) (lecture notes)
 - [A gallery of interesting IPython Notebooks](https://github.com/ipython/ipython/wiki/A-gallery-of-interesting-IPython-Notebooks)

# Brief tutorial on Python

## Python as a calculator

Once in the IPython notebook, if you type a simple mathematical expression and press Shift+Enter it will give the result of the expression:

```python
1 + 2 - 5
```

    -2

```python
import math  # use the import function to import the math library
math.sqrt(12)
```

    3.4641016151377544

```python
x = 1
y = 1 + math.pi
y
```

    4.141592653589793

## Main built-in datatypes in Python

- Bolleans: True, False
- NoneType: None
- Numbers: int, float, complex
- Sequences: list, tuple, range
- Text sequence: str
- Binary sequence: bytes, bytearray, memoryview
- Mapping: dict
- Set: set, frozenset
- Boolean operations: and, or, not
- Comparisons: <, <=, >, >=, ==, !=, is, is not
- Math operations: +, -, \*, /, //, %, **
- Bitwise operations: |, ^, &, <<, >>, ~

## Example: strings

```python
s = 'P' + 'y' + 't' + 'h' + 'o' + 'n'
print(s)
print(s*5)
```

    Python
    PythonPythonPythonPythonPython

Strings can be subscripted (indexed); like in C, the first character of a string has subscript (index) 0:

```python
print('s[0] = ', s[0], '  (s[index], start at 0)')
print('s[5] = ', s[5])
print('s[-1] = ', s[-1], '  (last element)')
print('s[:] = ', s[:], '  (all elements)')
print('s[1:] = ', s[1:], '  (from this index (inclusive) till the last (inclusive))')
print('s[2:4] = ', s[2:4], '  (from 1st index (inclusive) till 2nd index (exclusive))')
print('s[:2] = ', s[:2], '  (till this index, exclusive)')
print('s[:10] = ', s[:10], '  (Python handles the index if it''s larger than length)')
print('s[-10:] = ', s[-10:])
print('s[0:5:2] = ', s[0:5:2], '  (s[ini:end:step])')
print('s[::2] = ', s[::2], '  (s[::step], initial and final indexes can be omitted)')
print('s[0:5:-1] = ', s[::-1], '  (s[::-step] reverses the string)')
print('s[:2] + s[2:] = ', s[:2] + s[2:], '  (this sounds natural with Python indexing)')
```

    s[0] =  P   (s[index], start at 0)
    s[5] =  n
    s[-1] =  n   (last element)
    s[:] =  Python   (all elements)
    s[1:] =  ython   (from this index (inclusive) till the last (inclusive))
    s[2:4] =  th   (from 1st index (inclusive) till 2nd index (exclusive))
    s[:2] =  Py   (till this index, exclusive)
    s[:10] =  Python   (Python handles the index if its larger than length)
    s[-10:] =  Python
    s[0:5:2] =  Pto   (s[ini:end:step])
    s[::2] =  Pto   (s[::step], initial and final indexes can be omitted)
    s[0:5:-1] =  nohtyP   (s[::-step] reverses the string)
    s[:2] + s[2:] =  Python   (this sounds natural with Python indexing)

## Defining a function in Python

```python
def fibo(N):
    """Fibonacci series: the sum of two elements defines the next.

    The series is calculated till the input parameter N and
    returned as an ouput variable.

    """

    a, b, c = 0, 1, []
    while b < N:
        c.append(b)
        a, b = b, a + b

    return c
```

```python
fibo(9)
```

    [1, 1, 2, 3, 5, 8]

## Defining a function in Python II

```python
def bmi(weight, height):
    """Body mass index calculus and categorization.
    Enter the weight in kg and the height in m.
    See http://en.wikipedia.org/wiki/Body_mass_index
    """
    bmi = weight / height**2
    if bmi < 15:
        c = 'very severely underweight'
    elif 15 <= bmi < 16:
        c = 'severely underweight'
    elif 16 <= bmi < 18.5:
        c = 'underweight'
    elif 18.5 <= bmi < 25:
        c = 'normal'
    elif 25 <= bmi < 30:
        c = 'overweight'
    elif 30 <= bmi < 35:
        c = 'moderately obese'
    elif 35 <= bmi < 40:
        c = 'severely obese'
    else:
        c = 'very severely obese'

    s = 'For a weight of {0:.1f} kg and a height of {1:.2f} m,\n\
    the body mass index (bmi) is {2:.1f} kg/m2,\n\
    which is considered {3:s}.'\
    .format(weight, height, bmi, c)
    print(s)
```

```python
bmi(70, 1.90);
```

    For a weight of 70.0 kg and a height of 1.90 m,
        the body mass index (bmi) is 19.4 kg/m2,
        which is considered normal.

## Numeric data manipulation with Numpy

Numpy is the fundamental package for scientific computing in Python and has a N-dimensional array package convenient to work with numerical data. With Numpy it's much easier and faster to work with numbers grouped as 1-D arrays (a vector), 2-D arrays (like a table or matrix), or higher dimensions.

```python
import numpy as np

x = np.array([1, 2, 3, 4, 5, 6])
print(x)
x = np.random.randn(2,4)
print(x)
```

    [1 2 3 4 5 6]
    [[ 0.6504986   1.21639113  0.06680213  0.43133861]
     [ 0.35556254  0.43596075  1.17614962  1.0677548 ]]

## Moving-average filter (Numpy use for performance)

*A moving-average filter has the general formula:*

$$ y[i] = \sum_{j=0}^{m-1} x[i+j] \;\;\;\; for \;\;\; i=1, \; \dots, \; n-m+1 $$

Here are two different versions of a function to implement the moving-average filter:

```python
import numpy as np
def mav1(x, window):
    """Moving average of 'x' with window size 'window'."""
    y = np.empty(len(x)-window+1)
    for i in range(len(y)):
        y[i] = np.sum(x[i:i+window])/window
    return y

def mav2(x, window):
    """Moving average of 'x' with window size 'window'."""
    xsum = np.cumsum(x)
    xsum[window:] = xsum[window:] - xsum[:-window]
    return xsum[window-1:]/window
```

```python
x = np.random.randn(300)/10
x[100:200] += 1
window = 10

print('Performance of mav1:')
%timeit mav1(x, window)
print('Performance of mav2:')
%timeit mav2(x, window)
```

    Performance of mav1:
    1000 loops, best of 3: 1.56 ms per loop
    Performance of mav2:
    The slowest run took 5.70 times longer than the fastest. This could mean that an intermediate result is being cached.
    100000 loops, best of 3: 11.6 µs per loop

## Ploting with matplotlib

Matplotlib is the most-widely used packge for plotting data in Python. Let's see some examples of it.

```python
import matplotlib.pyplot as plt
#%matplotlib notebook
%matplotlib inline
import numpy as np
```

```python
y1 = mav1(x, window)
y2 = mav2(x, window)
# plot
fig, ax = plt.subplots(1, 1, figsize=(8, 4))
ax.plot(x,  'b-',  linewidth=1, label = 'raw data')
ax.plot(y1, 'y-',  linewidth=2, label = 'moving average 1')
ax.plot(y2, 'g--', linewidth=2, label = 'moving average 2')
ax.legend(frameon=False, loc='upper right', fontsize=12)
ax.set_xlabel("Data #")
ax.set_ylabel("Amplitude")
ax.grid();
```

```python
plt.figure(figsize=(8, 4))
plt.plot(x,  'b-',  linewidth=1, label = 'raw data')
plt.plot(y1, 'y-',  linewidth=2, label = 'moving average 1')
plt.plot(y2, 'g--', linewidth=2, label = 'moving average 2')
plt.legend(frameon=False, loc='upper right', fontsize=12)
plt.xlabel("Data #")
plt.ylabel("Amplitude")
plt.grid()
plt.show()
```

## Ploting with matplotlib II

Plot figure in an external window (outside the ipython notebook area):

```python
#%matplotlib qt
```

```python
mu, sigma = 10, 2
x = mu + sigma * np.random.randn(1000)
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(8, 4))
ax1.plot(x, 'ro')
ax1.set_title('Data')
ax1.grid()

n, bins, patches = ax2.hist(x, 25, normed=True, facecolor='r') # histogram
ax2.set_xlabel('Bins')
ax2.set_ylabel('Probability')
ax2.set_title('Histogram')
fig.suptitle('Another example using matplotlib', fontsize=18, y=1.02)
ax2.grid()

plt.tight_layout()
plt.show()
```

```python
# get back the inline plot
#%matplotlib inline
#%matplotlib notebook
```

Instead of "`%matplotlib inline`" you can use "`%matplotlib notebook`" which gives you a nice toolbar for zooming, panning, etc. The caveat is that once "`%matplotlib notebook`" is used you can't alternate between matplotlib backends as we just did.

## Symbolic mathematics with Sympy

Sympy is a package to perform symbolic mathematics in Python. Let's see some of its features:

```python
from IPython.display import display
import sympy as sym
from sympy.interactive import printing
printing.init_printing()
```

Define some symbols and the create a second-order polynomial function (a.k.a., parabola), plot, and find the roots:

```python
x, y = sym.symbols('x y')
y = -x**3 + 4*x
y
```

```python
from sympy.plotting import plot
%matplotlib inline
plot(y, (x, -3, 3));
```

```python
sym.solve(y, x)
```

## More live examples

Let's run stuff from:
- [https://github.com/demotu/BMC](https://github.com/demotu/BMC)
- [http://nbviewer.ipython.org/github/ipython/ipython/blob/master/examples/Index.ipynb](http://nbviewer.ipython.org/github/ipython/ipython/blob/master/examples/Index.ipynb)
- [http://nbviewer.jupyter.org/](http://nbviewer.jupyter.org/)
- ...

## Questions?

- http://mail.scipy.org/mailman/listinfo/ipython-dev
- http://www.reddit.com/r/python
- http://stackoverflow.com/

> This entire document was written in the Jupyter Notebook (which can be statically viewed [here](http://nbviewer.ipython.org/github/demotu/BMC/blob/master/notebooks/PythonForScientificComputing.ipynb) or downloaded [here](https://raw.githubusercontent.com/demotu/BMC/master/notebooks/PythonForScientificComputing.ipynb)). If you are watching my presentation right now, these slides are just a visualization of the same notebook (probably using the [RISE: "Live" Reveal.js Jupyter/IPython Slideshow Extension](https://github.com/damianavila/live_reveal)).


# Cass and Koopman's Model of Optimal Growth

** This project sets out to investigate the optimal level of growth. **

We're interested analysing the theoretically optimal level of optimal growth. To do so, we will be using Cass and Koopman's model of exactly that.

The model can be interpreted as an extension of the Solow model but adapted to make the savings rate the outcome of an optimal choice. This is in contrast to that of the Solow model which assumed a constant savings rate determined outside the model. The model is based on the articles:

* Tjalling C. Koopmans. On the concept of optimal economic growth. In Tjalling C. Koopmans, editor, The Economic Approach to Development Planning, page 225–287. Chicago, 1965.

* David Cass. Optimum growth in an aggregative model of capital accumulation. Review of Economic Studies, 32(3):233–240, 1965.

** Imports and set magics:**

```python
import numpy as np
from scipy import optimize
import sympy as sm
import matplotlib.pyplot as plt

# autoreload modules when code is run
%load_ext autoreload
%autoreload 2

# local modules
import modelproject
```

# Description of the model

Time is discrete and takes the values t=0,1...,T. A single good is consumed or invested in physical capital. The consumption good is not durable and will depreciate if it is not consumed immediately. The capital good is durable but depreciates each period with the rate $\gamma \epsilon (0,1)$.

We consider the a model of Cass and Koopman's optimal growth where:

* $C_t$ is a nondurable consumption good at time t.
* $K_t$ is is the stock of physical capital at time t.
* Let $C={C_0,...,C_T}$ and $K={K_1,...,K_{T+1}}$

A representative household is endowed with one unit of labour $N_t$ at each t, such that $N_t=1$ for all $t \epsilon [0,T]$.

The representative household has preferences over consumption bundles with the utility function given by:

$$ U(C)=\sum_{T=0}^{T}\beta^t\frac{C_t^{1-\gamma}}{1-\gamma} $$

where $\beta \epsilon (0,1)$ is a discount factor and $\gamma > 0$ decides the curvature of the one-period utility function.

Note that

$$u(C_t)=\frac{C_t^{1-\gamma}}{1-\gamma}$$

satisfies $u'>0, u''<0$.

We also note that
* $u'>0$ asserts the consumer prefers more to less
* $u''<0$ asserts that marginal utility declines with increases in $C_t$

We assume that $K_0>0$ is a given exogenous level of intial capital.

There is an economy-wide production function:

$$ F(K_t, N_t)=AK_t^\alpha N_t^{1-\alpha} $$

with 0 < \alpha < 1, A>0.

A feasible allocation C, K will satisfy
$$ C_t+K_t+1 \leq F(K_t,N_t)+(1-\delta)K_t,$$

for all $t \epsilon[0,T]$

where $\delta \epsilon(0,1)$ is the rate at which capital depreciates.

## Planning Problem

A planner chooses an allocation ${C,K}$, to maximise the utility function st the feasible allocation. Let $\mu ={\mu_0,...,\mu_T}$ be a sequence of non-negative Lagrange multipliers. To find an optimal allocation, we use the Lagrangian

$$ L(C, K, \mu) = \sum_{t=1}^{T}\beta^t{\mu(C_t)+\mu_t(F(K_t, 1)+(1-\delta)K_t-C_t-K_{t+1})} $$

and then solve the following max problem

$$ max L(C,K, \mu) $$

**Useful Properties of Linearly Homogenous Production Functions**

Notice that

$$ F(K_t, N_t)=AK^\alpha_tN^{1-\alpha}_t=N_tA(\frac{K_t}{N_t})^\alpha $$

We define the output per-capital production function

$$f(\frac{K_t}{N_t})=A(\frac{K_t}{N_t})^\alpha $$

whose argument is capital per-capita.

Then we have that

$$F(K_t,N_t)=N_tf(\frac{K_t}{N_t}) $$

Taking the derivate wrt K, yields

$$ \frac{\delta F}{\delta K} = \frac{\delta N_tf({\frac{K_t}{N_t})}}{\delta N_t} $$
$$ = N_tf'(\frac{K_t}{N_t}\frac{1}{N_t}) $$
$$ =f' (\frac{K_t}{N_t} \bigg\rvert_{N_t=1} $$
$$ f'(K_t) $$

Also

$$ \frac{\delta F}{\delta N} = \frac{\delta N_t f(\frac{K_t}{N_t})}{\delta N_t} $$
$$ = f(\frac{K_t}{N_t})+N_tf'(\frac{K_t}{N_t})-\frac{-K_t}{N_t^2}) $$
$$ = f(\frac{K_t}{N_t})- \frac{K_t}{N_t}f'(\frac{K_t}{N_t})\bigg\rvert_{N_t=1} $$
$$ = f(K_t)-f'(K_t)K_t $$

** Returning to solving the problem **

We compute first derivatives of Lagrangian and set them equal to 0, in order to solve the Lagrangian maximisation problem.

Our objective function and constraints satisfy conditions that assure that the required SOCs are satisfied at an allocation satisfying the FOCs which that are derived below.

The FOC for maximisation with respet to C, K:

$$ C_t: \mu'(C_t)=\mu_t = 0 for all t= 0,1,...,T $$
$$ K_t: \beta \mu_t[(1-\delta)+f'(K_t)]-\mu–{t-1}=0 for all t=1,2,...,T $$
$$ \mu_t:F(K_t,1)+(1-\delta)K_t-C_t-K_{t+1}=0 for all t=0,1,...,T $$
$$ K_{T+1}: -\mu_T 	\leq 0, 	\leq if K_{T+1}=0; =0 if K_{T+1} > 0 $$

In the equation for $C_t$ we plugged in for $\frac{\delta F}{\delta K}$ using the formula given above. As $N_t=1$ for all t=1,...,T, it is not necessary to differentiate with respect to those arguments. Note that the equation for $\mu_t$ comes from the occurrence of $K_t$ in both the period t and period t-1 feasibility constraints. The equation for $K_{T+1}$ comes from differentiating with respect to $K_{T+1}$ in the last period and applying the following Karush-Kuhn-Tucker condition:

$$ \mu_tK_{T+1}=0 $$

Combining equations for $C_t$ and $K_t$ yields

$$ \mu'(C_t)[(1-\delta)+f'(K_t)]-\mu'(C{t-1})=0 $$ for all t=1,2,...,T+1

Rewriting yields

$$ u'(C_{t+1})[(1-\delta)+f'(K_{t+1})]=\mu'(C_t) $$ for all t=0,1,...,T

Taking the inverse of the utility function on both sides of the above equation yields

$$ C_{t+1}=u'^{-1}((\frac{\beta}{u'(C_t)}[f'(K_{t+1})+(1-\delta)])^{-1}) $$

Or using the utility function.

$$ C_{t+1}=(\beta C^{\gamma}_t[f'(K_{t+1})+(1-\delta)])^{1/\gamma} $$
$$ =C_t(\beta[f'(K_{t+1})+(1-\delta)])^{1/\gamma} $$

The above FOC for consumption is an Euler Equation. It descirbes how consumption in consecuitive periods are optimally related to each other and to capital in the following period.

We now apply the equations above to calculate variables and functions that we will need to solve the planning problem.

First we define symbols

```python
gamma = sm.symbols('gamma')
alpha = sm.symbols('alpha')
delta = sm.symbols('delta')
beta = sm.symbols('beta')
A = sm.symbols('A')
```

```python
# The utility function
def u(c, gamma):
    if y == 1:  # if y = 1 we can with L'hopital's Rule show that the utility becomes log
        return np.log(c)
    else:
        return (c**(1-gamma))/(1-gamma)

#The derivative of utility
def u_prime(c, gamma):
    if gamma == 1:
        return 1/c
    else:
        return c**(-gamma)

#The inverse utility
def u_prime_inverse(c, gamma):
    if gamma == 1:
        return c
    else:
        return c**(-1/gamma)

#The production function
def f(A, k, alpha):
    return A*k**alpha

#The derivative of production function
def f_prime(A, k, alpha):
    return alpha*A*k**(alpha-1)

#The inverse production function
def f_prime_inverse(A, k, alpha):
    return (k/(A*alpha))**(1/(alpha-1))
```

We will be using an algorithmic method with a for loop, to derive an optimal allocation for C,K and an associated Lagrange multiplier sequence $\mu$. The FOCs for the planning problem, form a system of difference equations with two boundary conditions

* $K_0$ is a given initial condition for capital
* $K_{T+1}=0$ is a terminal condition for capital

The parameters are:
* c = Initial consumption
* k = Initial capital
* $\gamma$ = Coefficient of relative risk aversion
* $\delta$ = Depreciation rate on capital
* $\beta$ = Discount factor
* $\alpha$ = return to capital per capital
* A = technology

** The model paramters are defined **

```python
gamma=2
delta=0.02
beta=0.95
alpha=0.33
A=1
```

** The algortihmic method to solve the problem **

```python
T=10
c=np.zeros(T+1) #T periods of consumption initialised to 0
k=np.zeros(T+2) #T periods of capital initialised to 0(T+2 to include t+1 variable)
k[0]=0.3 #Initial k
c[0]=0.2 # Initial guess of c_0

def algorithm(c, k, gamma, delta, beta, alpha, A):
    T = len(c)-1
    for t in range(T):
        k[t+1]=f(A=A, k=k[t], alpha=alpha)+(1-delta)*k[t]-c[t]
        if k[t+1]<0: #Ensuring nonnegativity
            k[t+1]=0
        if beta*(f_prime(A=A, k=k[t+1], alpha=alpha)+(1-delta))==np.inf:
#Only occurs if k[t+1] is 0, at which point nothing will be produced next period, thus consumption go to 0
            c[t+1]=0
        else: c[t+1]=u_prime_inverse(u_prime(c=c[t], gamma=gamma)/(beta*(f_prime(A=A, k=k[t+1], alpha=alpha)+(1-delta))), gamma=gamma)

#Terminal condition calculation
    k[T+1]=f(A=A, k=k[T], alpha=alpha)+(1-delta)*k[T]-c[T]
    return c, k

paths = algorithm(c, k, gamma, delta, beta, alpha, A)

fig, axes = plt.subplots(1, 2, figsize=(10, 4))
colors = ['orange', 'green']
titles = ['Consumption', 'Capital']
ylabels = ['$c_t$', '$k_t$']

for path, color, title, y, ax in zip(paths, colors, titles, ylabels, axes):
    ax.plot(path, c=color, alpha=0.7)
    ax.set(title=title, ylabel=y, xlabel='t')

ax.scatter(T+1, 0, s=80)
ax.axvline(T+1, color='k', ls='--', lw=1)

plt.tight_layout()
plt.show()
```

From the graphs above, it is evident that our guess for $\mu_0$ is too high and makes initial consumption too low. This is evident because the $K_{T+1}=0$ target is missing on the high side.

## Bisection Method

In the following section we will automate the above procedure with the derivative-free method, Bisection. Applying the method means searching for $\mu_0$, stopping when we reach the target $K_{T+1}=0$.

We take an initial guess for $C_0$ ($\mu_0$ can be eliminated because $C_0$ is an exact function of $\mu_0$. We know that the lowest $C_0$ can ever be is 0 and the largest it can be is initial output $f(K_0)$. We will take a guess on $C_0$ towards T+1. If $K_{T+1}>0$, let it be our new lower bound on $C_0$. If $K_{T+1}<0$, let it be our new upper bound. We will make a new guess for $C_0$ exactly halfway between our new upper and lower bounds. When $K_{T+1}$ gets close enough to 0 (wihtin some error tolerance bounds), the procedure will stop and we will have our values for consumption and capital.

More specifically the bisection methods in our model works in the following steps:

1. We set $c_{low}=0$ and $c_{high}=f(k=k[0], alpha=alpha, A=A)$ where $f(c_{low})$ and $f(c_{high})$ have opposite sign, $f(c_{low})f(c_{high})<0$

2. We compute $C[0]$ where $C[0]=(c_{low}+c_{high})/2$ is the midpoint

3. The next sub-interval $[c_{low+1},c_{high+1}]$:

    - If $f(c_{low})f(C[0])<0$ (different signs) then $c_{low+1}=c_{low}$ and $c_{high+1}=C[0]$ (i.e. focus on the range $[c_{low},C[0]$)

    - If $fC[0]c_{high}<0$ (different signs) then $c_{low+1}=C[0]$ and $c_{high+1}=c_{high}$ (i.e. focus on the range $[C[0], c_{high}]$)

4. Steps 2 and 3 are then repeated until $f(C[0]_n)<\epsilon$

```python
def bisection(c, k, gamma, delta, beta, alpha, A, tol=1e-4, max_iter=1e4, terminal=0): # Terminal is the value we are estimating towards

    #Step 1: Initialise
    T = len(c) - 1
    i = 1                                    # Initial iteration
    c_high = f(k=k[0], alpha=alpha, A=A)     # Initial high value of c
    c_low = 0                                # Initial low value of c

    path_c, path_k = algorithm(c, k, gamma, delta, beta, alpha, A)

    #Step 2-4: Main
    while (np.abs((path_k[T+1] - terminal)) > tol or path_k[T] == terminal) and i < max_iter:

        # Step 2: Midpoint and associated value
        c[0] = (c_high + c_low) / 2
        path_c, path_k = algorithm(c, k, gamma, delta, beta, alpha, A)

        # Step 3: Determine sub-interval
        if path_k[T+1] - terminal > tol:
            # If assets are too high the c[0] guess is lower bound on possible values of c[0]
            c_low = c[0]
        elif path_k[T+1] - terminal < -tol:
            # If assets fell too quickly, the c[0] guess is upper bound on possible values of c[0]
            c_high=c[0]
        elif path_k[T] == terminal:
            # If assets fell too quickly, the c[0] guess is now an uppernbound on possible values of c[0]
            c_high=c[0]

        i += 1

    if np.abs(path_k[T+1] - terminal) < tol and path_k[T] != terminal:
        print('Bisection method successful. Converged on iteration', i-1)
    else:
        print('Bisection method failed')

    u = u_prime(c=path_c, gamma=gamma)
    return path_c, path_k, u
```

** Plots of the above defined algorithms **

```python
T = 10
c = np.zeros(T+1)
k = np.zeros(T+2)

k[0] = 0.3 # Initial k
c[0] = 0.3 # Initial guess of c_0

paths = bisection(c, k, gamma, delta, beta, alpha, A)

def plot_paths(paths, axes=None, ss=None):

    T = len(paths[0])

    if axes is None:
        fix, axes = plt.subplots(1, 3, figsize=(13, 3))

    ylabels = ['$c_t$', '$k_t$', '$\mu_t$']
    titles = ['Consumption Level', 'Capital Level', 'Lagrange Multiplier']

    for path, y, title, ax in zip(paths, ylabels, titles, axes):
        ax.plot(path)
        ax.set(ylabel=y, title=title, xlabel='t')

    #Plotting the steady state value of k
    if ss is not None:
        axes[1].axhline(ss, c='k', ls='--', lw=1)

    axes[1].axvline(T, c='k', ls='--', lw=1)
    axes[1].scatter(T, paths[1][-1], s=80)
    plt.tight_layout()

plot_paths(paths)
```

Evidently now, when our initial guess of $\mu_0$ is higher, we get a significantly different result.

# Analysis of the Steady State

We now want to analyse the steady state of the model. We set the inital level of capital to its steady state.

If T $\rightarrow+ \infty$, the optimal allocation will converge to the steady state values of $C_t$ and $K_t$.

We can derive these values and set $K_0$ equal to its steady state value. In a steady state we have that $K_{t+1}=K_t=\overline{K}$ for all very large ts, the feasibility constraint previously stated is $f(\overline{K})-\delta\overline{K}=\overline{C}$ Substituting $K_t=\overline{K}$ and $C_t=\overline{C}$ for all t into the previously obtained equation $$u'(C_{t+1})[(1-\delta)+f'(K_{t+1})]= u'(C_t)$$ for all t=0,1,...,T, yields $$1=\beta\frac{u'(\overline{C}}{u'(\overline{C}}[f'(\overline{K}+(1-\delta)]$$. Defining $\beta=\frac{1}{1+\rho}$, and rearranging yields $$1+\rho=1[f'(\overline{K})+(1- \delta)]$$ Simplifying yields $$f'(\overline{K})=\rho+\delta$$ and $$\overline{K}=f'^{-1}(\rho+\delta)$$ Using our production function from earlier yields $$\alpha\overline{K}^{\alpha-1}=\rho+\delta$$

Using the obtained values $\alpha$=0.33, $\rho=\frac{1}{\beta}-1=\frac{1}{\frac{19}{20}}-1=\frac{1}{19}$ and $\delta=\frac{1}{50}$, we get $$\overline{K}=(\frac{\frac{33}{100}}{\frac{1}{50}+\frac{1}{19}})^{\frac{67}{100}}≈9.6$$

In the below we will verify this result and use this steady state $\overline{K}$ as our initial capital stock $K_0$.

```python
rho = sm.symbols('rho')
rho=1/beta-1
k_ss=f_prime_inverse(k=rho+delta,A=A, alpha=alpha)
print(f'The steady state of capital, k, is: {k_ss}')
```

    The steady state of capital, k, is: 9.57583816331462

** We are now at a stage where we can plot given the obtained values for the steady state **

```python
T=150
c=np.zeros(T+1)
k=np.zeros(T+2)
c[0]=0.3
k[0]=k_ss
paths = bisection(c, k, gamma, delta, beta, alpha, A)

plot_paths(paths, ss=k_ss)
```

From the plots obtained above we see that in this economy with a large value of $T$, $K_t$ will stay near its initial value for as long as possible. We can from this deduct that the social planner likes the steady state capital stock and wants to stay there for as long as possible.

# Changing the parameter values

In the below we examine what happens when the initial $K_0$ is pushed below $\overline{K}$.

```python
k_initial = k_ss/3 #Value below steady state
T=150
c=np.zeros(T+1)
k=np.zeros(T+2)
c[0]=0.3
k[0]=k_initial
paths = bisection(c, k, gamma, delta, beta, alpha, A)

plot_paths(paths, ss=k_ss)
```

We now see that the planner pushes capital toward the steady state value then stays at this value for a substantial amount of time and subsequently pushes $K_t$ toward the terminal value $K_{T+1}=0$ as t gets close to T.

## Changing the value of T

We are also interested in seeing how the trajectory of the paths will change, when altering the value for T. We're making a list with four differet values of T in order to see the difference when the time horizon is altered within the same graphs. The values of T are ranging from 30 to 180 to incapsule a large range of Ts.

```python
T_list = (180, 90, 60, 30)
fix, axes =plt.subplots(1, 3, figsize=(13,3))

for T in T_list:
    c=np.zeros(T+1)
    k=np.zeros(T+2)
    c[0]=0.3
    k[0]=k_initial
    paths=bisection(c, k, gamma, delta, beta, alpha, A)
    plot_paths(paths, ss=k_ss, axes=axes)

```

The different colours in the graphs above are tied to outcomes with different time horizons T. These are the values given in the T_list.

We see that as we increase the time horizon, the planner puts $K_{t}$ closer to the steady state value $\overline{K}$ for longer.

## Further changes to the value of T

** In the following we are testing what happens when we set the value of T at a very high value. **

We expect the pllaner making the capital stockspend most of its time close to its steady state level.

```python
T_list = (260, 180, 60, 30)
fix, axes = plt.subplots(1, 3, figsize=(13, 3))

for T in T_list:
    c = np.zeros(T+1)
    k = np.zeros(T+2)
    c[0] = 0.3
    k[0] = k_initial
    paths = bisection(c, k, gamma, delta, beta, alpha, A)
    plot_paths(paths, ss=k_ss, axes=axes)
```

Evidently the bisection method failed when the parameter for T is set to 260. It failed to converge and hit the maximum iteration.

However, it is evident that the pattern from the previous analysis is repeated with the increased values of T. The pattern reflects a turnpike property of the steady state.

We can conclude that for any given initial value of $K_0$, $K_t$ is pushed toward the steady state and held at this level for as long as possible.

# Further analysis

In the below we extend the Cass-Koopman's model of optimal growth by adding an environmental term with inspiration from a paper done by Luiz Fernando (Luiz Fernando Ohara Kamogawa & Ricardo Shirota, 2011. "Economic growth, energyconsumption and emissions: an extension of Ramsey-Cass-Koopmans modelunder EKC hypothesis," Anais do XXXVII Encontro Nacional de Economia [Proceedings of the 37th Brazilian Economics Meeting] 187, ANPEC)

## Description of the model extension

The production function remains unchanged, but new parameters changes the utility function. The parameters are:

* $\eta$ = relative $CO_2$-emission done by non-renewable energy compared to renewable energy
* $\Phi$ = means of the gloabl awareness of climate changes
* $j$ = substitutability from non-renewable energy to renewable energy.

The utility function is then given by:

$$U=c^{-\gamma}-\eta*\Phi^j$$

## Solving the model

To solve the extended model, we use the same bisection method as previous, but add the above mentioned alterations.

**The new symbols are defined:**

```python
epsilon = sm.symbols('epsilon')
theta = sm.symbols('theta')
```

**The parameters are defined:**

The values of the added terms is based on empirical studies found in the literature.

```python
epsilon=0.5
theta=0.3
j=0.5
```

**The model functions are defined:**

```python
#The derivative of environmental utility
def u_prime_2(c, gamma, epsilon, theta, j):
    if gamma == 1:
        return 1/c
    else:
        return c**(-gamma)-epsilon*(theta)**j

#Inverse environmental utility
def u_prime_inverse_2(c, gamma, epsilon, theta, j):
    if gamma == 1:
        return c
    else:
        return c**(-1/gamma)-epsilon*(theta)**(1/j)
```

**The bisection method is now used to solve the model:**

```python
def bisection(c, k, gamma, delta, beta, alpha, A, tol=1e-4, max_iter=1e4, terminal=0): # Terminal is the value we are estimating towards

    #Step 1: Initialise
    T = len(c) - 1
    i = 1                                    # Initial iteration
    c_high = f(k=k[0], alpha=alpha, A=A)     # Initial high value of c
    c_low = 0                                # Initial low value of c

    path_c, path_k = algorithm(c, k, gamma, delta, beta, alpha, A)

    #Step 2-4: Main
    while (np.abs((path_k[T+1] - terminal)) > tol or path_k[T] == terminal) and i < max_iter:

        # Step 2: Midpoint and associated value
        c[0] = (c_high + c_low) / 2
        path_c, path_k = algorithm(c, k, gamma, delta, beta, alpha, A)

        # Step 3: Determine sub-interval
        if path_k[T+1] - terminal > tol:
            c_low = c[0]
        elif path_k[T+1] - terminal < -tol:
            c_high=c[0]
        elif path_k[T] == terminal:
            c_high=c[0]

        i += 1

    if np.abs(path_k[T+1] - terminal) < tol and path_k[T] != terminal:
        print('Bisection method successful. Converged on iteration', i-1)
    else:
        print('Bisection method failed')

    u_2 = u_prime_2(c=path_c, gamma=gamma, theta=theta, j=j, epsilon=epsilon)
    return path_c, path_k, u_2

    T = 10
c = np.zeros(T+1)
k = np.zeros(T+2)

k[0] = 0.3 # Initial k
c[0] = 0.3 # Initial guess of c_0

paths = bisection(c, k, gamma, delta, beta, alpha, A)

def plot_paths(paths, axes=None, ss=None):

    T = len(paths[0])

    if axes is None:
        fix, axes = plt.subplots(1, 3, figsize=(13, 3))

    ylabels = ['$c_t$', '$k_t$', '$\mu_t$']
    titles = ['Consumption Level', 'Capital Level', 'Lagrange Multiplier']

    for path, y, title, ax in zip(paths, ylabels, titles, axes):
        ax.plot(path)
        ax.set(ylabel=y, title=title, xlabel='t')

    #Plotting the steady state value of k
    if ss is not None:
        axes[1].axhline(ss, c='k', ls='--', lw=1)

    axes[1].axvline(T, c='k', ls='--', lw=1)
    axes[1].scatter(T, paths[1][-1], s=80)
    plt.tight_layout()

plot_paths(paths)
```

The optimal consumption path has evidently changed to lower consumption in the fist 9 periods then a quite radical rise in the tenth period after having added the environmental terms. The optimal capital path has also changed to a bigger fall of capital in the last period.

# Conclusion

We used a algorithmic and bisection method to solve cass-koopman model for optimal growth. The optimal growth path for consumption is found to be rise over the whole time period while the path for capital rise until steady state is achieved then fall to zero at the end of the time period. The steady state for capital is found to be 9.58. In the next step we did a graphically visualization of the optimal consumption and capital path with different time horizons, we found evidence that the time period has to be larger than 150 years to be in the steady state path. At last we analyzed an extension to the baseline model, we added an environmental term to the utility function. The new environmental growth model had different optimal consumption and capital path.