### Primer examen parcial

##  Simulación matemática.
-  Lázaro Alonso
-  Año : 2017
-  Email:    `alonsosilva@iteso.mx, lazarus.alon@gmail.com`
___
#### Estudiante:

## Diego Parra Pereda ic698296

### Indicaciones:
- Todos  aquellos cuya inicial de su primer apellido este entre la *A* y la *L* tienen que hacer la **Actividad 1.1** del **problema 1. **
- Todos  aquellos cuya inicial de su primer primer apellido este entre la *M* y la *Z* tienen que hacer la **Actividad 1.2** del **problema 1. **
- Todos  aquellos cuya inicial de su primer apellido este entre la *M* y la *Z* tienen que hacer el **problema 2. **
- Todos  aquellos cuya inicial de su primer apellido este entre la *A* y la *L* tienen que hacer el **problema 3. **

**El objetivo final es que logren juntar 60 puntos. Es decir resolver 2 problemas por completo. **

### Problema 1.

Considere un oscilador armónico simple(péndulo), cuya variación en $\theta$ está dada por:

\begin{equation}
\theta(t) = \theta(0) \cos(\omega_{0} t) + \frac{\dot{\theta}(0)}{\omega_{0}} \sin(\omega_{0} t)
\end{equation}

Considere la siguiente función que calcula $\theta$ en $t$. Para responder las siguientes actividades.

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

```python
def theta_t(theta_0, theta_0_dot, g, l, t):
    omega_0 = np.sqrt(g/l)
    return theta_0 * np.cos(omega_0 * t) + theta_0_dot * np.sin(omega_0 * t)/omega_0
```

Ahora si gráficamos al péndulo en las coordenas $x,y$, se obtiene algo similar a lo siguiente:

```python
t = np.linspace(0,10)
```

```python
fig = plt.figure(figsize = (5,5))
ax = fig.add_subplot(1, 1, 1)

x = 2 * np.sin(theta_t(.4, .6, 9.8, 2, t))
y =  - 2 * np.cos(theta_t(.4, .6, 9.8, 2, t))
ax.plot(x, y, 'ko', ms = 1.5)
ax.plot([0], [0], 'rD')
ax.set_xlim(xmin = -2.2, xmax = 2.2)
ax.set_ylim(ymin = -2.2, ymax = .2)
plt.show()
```

**Actividad 1.1 **
- Modificar el programa anterior para que el pédulo describa una trayectoria completamente circular. **(10 puntos)**
- Indicar en el caso anterior con código de colores, los momentos en que el péndulo se encuetra por arriba del cero en $y$ y cuando se encuentra por debajo. **(10 puntos)**
- Considere un conjunto de péndulos, con las siguientes longitudes:
$$ l = [0.5, 1, 2, 4, 8, 16]. $$
Realice una representación similar a la anterior, pero ahora para todos los péndulos representados en la misma gráfica y cada uno con diferente color. Discutir resultados.**(10 puntos)**

**Actividad 1.2 **
- Modificar el programa anterior para que el pédulo describa una trayectoria semi-circular. **(10 puntos)**
- Indicar en el caso anterior con código de colores, los momentos en que el péndulo se encuetra tienes coordenas en $x$ negativas y cuando se encuentra en positivas.  **(10 puntos)**
- Considere un conjunto de péndulos con la misma longitudes, pero con diferentes gravedades:
$$ g = [0.5, 1, 2, 4, 8, 16]. $$
Realice una representación similar a la anterior, pero ahora para todos los péndulos representados en la misma gráfica y cada uno con diferente color. Discutir resultados. **(10 puntos)**

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

```python
def theta_t(theta_0, theta_0_dot, g, l, t):
    omega_0 = np.sqrt(g/l)
    return theta_0 * np.cos(omega_0 * t) + theta_0_dot * np.sin(omega_0 * t)
```

```python
t = np.linspace(0,10)
```

```python
fig = plt.figure(figsize = (7,5))
ax = fig.add_subplot(1, 1, 1)

x = 2 * np.sin(theta_t(1.57, .6, 9.8, 2, t))
y =  - 2 * np.cos(theta_t(1.57, .6, 9.8, 2, t))

ax.plot(x, y, 'bd', ms = 5)
ax.plot([0], [0], 'gD')

ax.set_xlim(xmin = -2.2, xmax = 2.2)
ax.set_ylim(ymin = -2.2, ymax = .2)

plt.show()
```

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

```python
def theta_t(theta_0, theta_0_dot, g, l, t):
    omega_0 = np.sqrt(g/l)
    return theta_0 * np.cos(omega_0 * t) + theta_0_dot * np.sin(omega_0 * t)
```

```python
t = np.linspace(0,10)
```

```python
fig = plt.figure(figsize = (7,5))
ax = fig.add_subplot(1, 1, 1)
x = 2 * np.sin(theta_t(1.57, 0, 9.8, 2, t))
y =  - 2 * np.cos(theta_t(1.57, 0, 9.8, 2, t))
ax.plot(x, y, 'bo', ms = 3)
ax.plot([0], [0], 'gD')
ax.set_xlim(xmin = -3, xmax = 3)
ax.set_ylim(ymin = -3, ymax = .3)
plt.show()
```

```python
z=x[x<0]
z
```

    array([-0.30186704, -1.9983598 , -1.02749371, -0.14489145, -1.39360214,
           -1.86161016, -0.04106839, -0.55933681, -1.99448644, -0.89678283,
           -0.22379964, -1.51784312, -1.75725738, -0.07448834, -0.80619655,
           -1.96541933, -0.76954944, -0.31260143, -1.6341099 , -1.62394097,
           -0.09702228, -1.0379322 , -1.91350047, -0.64744785])

```python
len(x[x<0])
```

    24

```python
np.where(x<0)
```

    (array([ 4,  5,  6,  8,  9, 10, 14, 18, 19, 20, 22, 23, 24, 28, 32, 33, 34,
            36, 37, 38, 42, 46, 47, 48], dtype=int64),)

```python
x1=x[np.where(x<0)]
```

```python
x2=x[np.where(x>0)]
```

```python
y2=y[np.where(x>0)]
```

```python
y1=y[np.where(x<0)]
```

```python
fig = plt.figure(figsize = (6,3))

ax = fig.add_subplot(1, 1, 1)

ax.plot(x1, y1, 'm*')
ax.plot(x2, y2, 'cd')

ax.set_xlim(xmin = -3, xmax = 3)
ax.set_ylim(ymin = -3, ymax = .3)

plt.show()
```

```python
gravedad = np.array ([.5, 1, 2, 4, 8, 16])
```

```python
def theta_t(theta_0, theta_0_dot, g, l, t):
    omega_0 = np.sqrt(g/l)
    return theta_0 * np.cos(omega_0 * t) + theta_0_dot * np.sin(omega_0 * t)
for indx, g in enumerate (gravedad):
    x = 2 * np.sin(theta_t(.4, .6, g, 2, t))
    y =  - 2 * np.cos(theta_t(.4, .6, g, 2, t))
(x)
(y)
```

    array([-1.84212199, -1.57677382, -1.51605737, -1.72746099, -1.96956477,
           -1.94483457, -1.68463644, -1.50548102, -1.61004451, -1.88161607,
           -1.99691442, -1.81044119, -1.55512314, -1.52867729, -1.76045012,
           -1.98366094, -1.92206326, -1.65368812, -1.50229101, -1.63810197,
           -1.90884522, -1.98938026, -1.77779414, -1.53683788, -1.54507564,
           -1.79336931, -1.99351017, -1.89624236, -1.62450615, -1.50328465,
           -1.66818486, -1.93327734, -1.97748629, -1.74476879, -1.52220435,
           -1.56499714, -1.82563092, -1.99891792, -1.86786376, -1.5975737 ,
           -1.50844681, -1.69979106, -1.95444438, -1.96146692, -1.7119499 ,
           -1.51144928, -1.58812811, -1.85664942, -1.99977701, -1.8374612 ])

```python
cmap= ['red','blue','green','black','orange',]
```

```python
fig = plt.figure(figsize = (15,10))
for indx, g in enumerate (gravedad):
    ax = fig.add_subplot(1, 1, 1)
    x = 2 * np.sin(theta_t(1.57, 0, g, 2, t))
    y =  - 2 * np.cos(theta_t(1.57, 0, g, 2, t))
    plt.scatter(x,y, cmap = 'inferno', s= 35, lw=0)
ax.plot(x, y, 'k*', ms = 7)
ax.plot([0], [0], 'gD')
ax.set_xlim(xmin = -2.2, xmax = 2.2)
ax.set_ylim(ymin = -2.2, ymax = .2)
plt.show()
```

```python

```

```python

```

### Problema 2.

**Ley de Newton de enfriamiento**

La ley empírica de Newton, relativa al enfriamiento de un objeto, se expresa con la ecuación diferencial lineal de primer orden

$$\frac{dT}{dt} = k (T - T_m)$$

donde $k$ es una constante de proporcionalidad, $T$ es la temperatura del objeto para $t>0$

```python
def temperatura(T, Tm, k, t):
    return k*(T - Tm)
```

```python
# usar odeint para encontrar la solución. (Puede ayudarle la clase de mapa logistico)
```

```python
import numpy as np
from scipy.integrate import odeint
import matplotlib.pyplot as plt
%matplotlib inline
```

**Actividad 2.1**
¿Cuánto esperar para tomar el café?

Primero calentamos agua a  80°C
 . Posteriormente agregamos café al vaso con el agua caliente. Después realizamos la medición de la temperatura ambiente, la cual fue de  24°C.

- Simula el sistema en un tiempo de  0
  a  120 unidades de tiempo con una constante de proporcionalidad  $k=−0.0565$. **(10 puntos)**
-  Supoga que cada unidad de tiempo corresponde a un minuto. ¿En que tiempo aproximadamente la temperatura es menor a  30°C
 ? **(10 puntos)**
-  Busca una constante de proporcionalidad  $k$
  en un rango ( de  −0.2
  a  0.2
  con incremento de 0.01), para la cual el café tiene una temperatura menor de  30°C
  en un tiempo a 20 minutos. **(10 puntos)**

```python
def temperatura(T,t):
    k=-.0565
    Tm=34
    return k*(T-Tm)
t = np.linspace(0,120)
T0=80
t = np.linspace(0,120)
respuesta = odeint(temperatura,T0,t)
plt.plot(t,respuesta)
plt.xlabel('$t$', fontsize = 40)
plt.ylabel('$T$', fontsize = 40)
plt.show()
```

### Problema 3.
**Interés simple vs interés compuesto. **

- Simple
> Recuerden $C_k$ es la meta
$$C_k=C_0(1+ki).$$

- Compuesto
> $$C_k=C_0(1+i)^k.$$

** Actividad 3.1**
- Realice un análisis comparativo del numero de periodos para llegar a la misma meta, considerenado el mismo interés $i$. Gráfique en la misma figura.  **(10 puntos)**
- Considere diferentes escenarios de interés. Usted elija.  **(10 puntos)**
- Discuta las ventajas o desventajas de los diferentes modelos. En el supuesto de que ustedes son el banco. **(10 puntos)**

```python
# Respuesta

```

```python

```

```python

```

```python

```

```python

```


# FROM THOUGHT TO CODE

This notebook is a case study of how to develop code to solve a problem.

We see that to implement a testbed for the above diagram, we need to construct the
signals $D(s)$ and $N(s)$.
We model both as sinusoids, but at different frequences.
$D(s)$ is generally low frequency (since we change the reference infrequently),
and $N(s)$ is general high frequency.

So, we want a way to easily incorporate different sinusoidal signals into the computational
framework.

Beyond this, there are other elements we'll need to build.
For example, the circles with a "+" are adders.
So, we need elements that take 2 or more inputs and output the sum of those inputs.

# Preliminaries

```python
!pip install -q controlSBML

import control
import controlSBML as ctl
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import sympy
import tellurium as te
```

```python
TIMES = ctl.makeSimulationTimes(0, 5, 500)
```

# Helpers

```python
def plotStep(tf, is_plot=True):
    """
    Plots a transfer function step response

    Parameters
    ----------
    tf: control.TransferFunction
    """
    result = control.forced_response(tf, T=TIMES, U=1)
    if is_plot:
        plt.plot(result.t, result.outputs)

# TESTS
Gs = control.TransferFunction([2], [1, 3])
plotStep(Gs, is_plot=False)
print("OK!")
```

    OK!

# Create Sine Waves

# Creating ``NonlinearIOSystem`` objects for Sine Waves

# Factory for ``NonlinearIOSystem`` Sine Wave

# Factory for ``NonlinearIOSystem`` Adder


```python
import numpy as np
import scipy as sp
from scipy import linalg
from scipy import optimize
from scipy import interpolate
import sympy as sm
import ipywidgets as widgets
sm.init_printing(use_unicode=True)
from IPython.display import display, Latex, Markdown

%matplotlib inline
import matplotlib.pyplot as plt
from matplotlib import cm
from mpl_toolkits.mplot3d import Axes3D
```

# 1. Human capital accumulation

Consider a worker living in **two periods**, $t \in \{1,2\}$.

In each period she decides whether to **work ($l_t = 1$) or not ($l_t = 0$)**.

She can *not* borrow or save and thus **consumes all of her income** in each period.

If she **works** her **consumption** becomes:

$$c_t = w h_t l_t\,\,\text{if}\,\,l_t=1$$

where $w$ is **the wage rate** and $h_t$ is her **human capital**.

If she does **not work** her consumption becomes:

$$c_t = b\,\,\text{if}\,\,l_t=0$$

where $b$ is the **unemployment benefits**.

Her **utility of consumption** is:

$$ \frac{c_t^{1-\rho}}{1-\rho} $$

Her **disutility of working** is:

$$ \gamma l_t $$

From period 1 to period 2, she **accumulates human capital** according to:

$$ h_2 = h_1 + l_1 +
\begin{cases}
0 & \text{with prob. }0.5 \\
\Delta & \text{with prob. }0.5
\end{cases} \\
$$

where $\Delta$ is a **stochastic experience gain**.

In the **second period** the worker thus solves:

$$
\begin{eqnarray*}
v_{2}(h_{2}) & = &\max_{l_{2}} \frac{c_2^{1-\rho}}{1-\rho} - \gamma l_2
\\ & \text{s.t.} & \\
c_{2}& = & w h_2 l_2 \\
l_{2}& \in &\{0,1\}
\end{eqnarray*}
$$

In the **first period** the worker thus solves:

$$
\begin{eqnarray*}
v_{1}(h_{1}) &=& \max_{l_{1}} \frac{c_1^{1-\rho}}{1-\rho} - \gamma l_1 + \beta\mathbb{E}_{1}\left[v_2(h_2)\right]
\\ & \text{s.t.} & \\
c_1 &=& w h_1 l_1 \\
h_2 &=& h_1 + l_1 + \begin{cases}
0 & \text{with prob. }0.5\\
\Delta & \text{with prob. }0.5
\end{cases}\\
l_{1} &\in& \{0,1\}\\
\end{eqnarray*}
$$

where $\beta$ is the **discount factor** and $\mathbb{E}_{1}\left[v_2(h_2)\right]$ is the **expected value of living in period two**.

The **parameters** of the model are:

```python
rho = 2
beta = 0.96
gamma = 0.1
w = 2
b = 1
Delta = 0.1
```

The **relevant levels of human capital** are:

```python
h_vec = np.linspace(0.1,1.5,100)
```

**Question 1:** Solve the model in period 2 and illustrate the solution (including labor supply as a function of human capital).

First we define the relevant functions:
The method moving on is rgeatly inspired from problemset 7 and lecture 2

```python
## Defining relevant functions

# a. defining utility of consumption
def utility_of_c(c,rho):
    """
    The function of utility of consumption c

    Args:
        c (float): Consumption
        rho (float): Measures the degree of relative risk aversion that is implicit in the utility function

    Returns:
           The utility of consumption given the relative risk aversion.
    """
    return c**(1-rho)/(1-rho)

# b. disutility of work
def disutility_of_work(gamma,l):
    """
    The disutility of work

    Args:
        gamma (float): coeficient of disutility of work
        l (int,boolean): Register if agents is working l_t = 1 or not l_t = 0

    Returns disutility
    """
    return -gamma*l
# c. define function of consumption
def c(w,h,l,b):
    """
    consumption function

    Args:
        w (float): Wage when working
        h (float, list): Level of human capital
        b (float): Benefits of not working
        l (int,boolean): ...

    Retruns:
        The consumption function given work or no work
    """
    if l==1:
        c = w*h*l # consumption function if working
    else:
        c = b # consumption function if not working
    return c
```

```python
# define netutility given human capital and labour
def v2(w,h2,l2,rho,gamma,b):
    """
    Net utility in second period

    Args:
        w (float): ...
        h2 (float, list): Human capital second periode
        l2 (int, boolean): dummy for work in second period
        rho (float): ...
        gamma (float): ...
        b(float): ...

    Returns: The sum of utility and disutility in the second periode given human capital, h2 and labour, l2

    """
    return utility_of_c(c(w,h2,l2,b),rho) + disutility_of_work(gamma,l2)
```

```python
# Define
def solve_periode_2(w,h_vec,rho,gamma,b,Delta):
    """
    function for solution in second period: sloves the model in the second periode

    Args:
        w (float): ...
        h_vec (list,tuple,ndarray): Vector of the different values of human capital
        rho (float): ...
        gamma (float): ...
        b (float):...
        Delta (float): Is a value to determine high and low accumulation of human capital

    Returns:
        l2_vec which is a vector (list) of the optimal labour supply given different values of h.
        v2_vec which is a vecotor (ndarray) of the net-utility given different values of both l and h.
    """
    # a. Grids
    # i. set the number of elements in array
    N = 100

    # ii. Create 3 new empty arrays of 100 elemnts without initializing entries
    l2_vec = np.empty(N)
    v2_vec = np.empty(N)

    # b. sets l2 = 1 for every h in h_vec that execute a higher utility working than not working.
    # Otherwise let L2 = 0
    for i,h2 in enumerate(h_vec):
        if v2(w,h2,1,rho,gamma,b) >= v2(w,h2,0,rho,gamma,b):
            l2_vec[i] = 1
        else:
            l2_vec[i] = 0
    #c. Generate a vector for net-utility in second period
        v2_vec[i] = v2(w,h2,l2_vec[i],rho,gamma,b)

    return v2_vec,l2_vec

# d. call the optimal labour supply, net-utility and consumption
l2_vec, v2_vec = solve_periode_2(w,h_vec,rho,gamma,b,Delta)

# e. construct interpolator (function based on fixed data points)
v2_interp=interpolate.RegularGridInterpolator((h_vec,),v2_vec, bounds_error=False,fill_value=None)
```

```python
## Making vectors of human capital to display the utility of working and not working
#if consumer choses to work
h2_vec_l = h_vec + 1 + Delta*0.5

#if he chooses not to work
h2_vec_ = h_vec + 0 + Delta*0.5

emptyarray = np.empty(100) # c
nowork_utility = []
for i in emptyarray:
    nowork_utility.append(v2(w,h2_vec_ ,0,rho,gamma,b))
```

```python
# c. plot
#Sub_fig_1
fig1_1 = plt.figure()
ax1 = fig1_1.add_subplot(1,1,1)
ax1.plot(h2_vec_, v2(w,h2_vec_,1,rho,gamma,b), label = 'If $l_2=1$')
plt.plot(h2_vec_, nowork_utility         , label = 'If $l_2=0$')
ax1.plot(h_vec, l2_vec, label = 'Optimal utility')
ax1.legend(loc='lower right')

ax1.grid()
ax1.set_xlabel('$h_2$')
ax1.set_ylabel('Net Utility')
ax1.set_title('Net-Utiliy function in period 2')
ax1.set_xlim([0,1.7])
ax1.set_ylim([-2,0]);

#sub_fig_2
fig1_2 = plt.figure()
ax2 = fig1_2.add_subplot(1,1,1)
ax2.plot(h_vec, v2_vec, label = 'Labour supply')
ax2.grid()
ax2.set_xlabel('$h_2$')
ax2.set_ylabel('Labor supply')
ax2.set_title('Labour supply')
ax1.legend(loc='lower right')

```

The green line in the first figure display the optimal utility when only choosing to work after the threshold. As a consequense the second figure display the jump in labour supply from 0 to 1 the moment it gets advantageous to work which is approximately h_2 = 0.56.

**Question 2:** Solve the model in period 1 and illustrate the solution (including labor supply as a function of human capital).

```python
def v1(w,l1,h1,rho,beta,gamma,Delta,b,v2_interp):
    '''
    function for the net utility of the first period given the expected net utility in second period

    Args:
        w (float): ...
        l1 (int, boolean): Dummy for working in period 1
        h1 (float, list): human capital accumulated in periode 1
        rho (float): ...
        beta (float): ...
        gamma (float): ...
        Delta (float): ...
        b (float): ...
        v2_interp (ndarray): is the net utility of periode 2

    Return: The net utility of periode 1.
    '''

    # a. v2 value, if working but low human capital
    h2_low = h1 + l1 + 0
    v2_low = v2_interp([h2_low])[0]

    # b. v2 value, if working and having high human capital
    h2_high = h1 + l1 + Delta
    v2_high = v2_interp([h2_high])[0]

    # c. expected v2 value
    v2 = 0.5*v2_low + 0.5*v2_high

    # d. net utility
    return utility_of_c(c(w,h1,l1,b),rho) + disutility_of_work(gamma,l1) + beta*v2-1
```

```python
# periode 1

def solve_periode_1(w,h_vec,rho,beta,gamma,Delta,b,v2_interp):
    """
    function for solution in second period: sloves the model in the second periode

    Returns:
        l1_vec which is a vector (list) of the optimal labour supply given different values of h.
        v1_vec which is a vecotor (ndarray) of the net-utility given different values of both l and h.
    """
    # a. Grids
    # i. set the number of elements in array
    N=100

    # ii. Create 3 new empty arrays of 100 elemnts without initializing entries
    l1_vec = np.empty(N)
    v1_vec = np.empty(N)
    no_work = np.empty(N)

    # b. sets l1 = 1 for every h in h_vec that execute a higher utility working than not working.
    # Otherwise let L1 = 0
    for i, h1 in enumerate(h_vec):
        if v1(w,1,h1,rho,beta,gamma,Delta,b,v2_interp) >= v1(w,0,h1,rho,beta,gamma,Delta,b,v2_interp):
            l1_vec[i] = 1
        else:
            l1_vec[i] = 0
    #c. Generate a vector for net-utility in first period
        v1_vec[i] = v1(w,l1_vec[i],h1,rho,beta,gamma,Delta,b,v2_interp)

    return v1_vec,l1_vec
# d. call the optimal labour supply, net-utility and consumption
v1_vec, l1_vec = solve_periode_1(w,h_vec,rho,beta,gamma,Delta,b,v2_interp)
```

```python
# plot
#Sub_fig_1
fig2_1 = plt.figure()
ax1 = fig2_1.add_subplot(1,1,1)
ax1.plot(h_vec, v1_vec, label = 'Optimal utility')

ax1.grid()
ax1.set_xlabel('$h_1$')
ax1.set_ylabel('Net Utility')
ax1.set_title('Net-Utiliy function in period 1')

#sub_fig_2
fig2_2 = plt.figure()
ax2 = fig2_2.add_subplot(1,1,1)
ax2.plot(h_vec, l1_vec, label = 'Labour supply')

ax2.grid()
ax2.set_xlabel('$h_1$')
ax2.set_ylabel('Labor supply')
ax2.set_title('Labour supply')
```

It is the same result as in periode 2, that for low enough human capital you wont work.

**Question 3:** Will the worker never work if her potential wage income is lower than the unemployment benefits she can get? Explain and illustrate why or why not.

By using the plots already shown we can state that with benefits higher than wages the agent will still choose to work given high enough human capital.

```python
def interactive_figure(w,beta,b,):

    l2_vec, v2_vec = solve_periode_2(w,h_vec,rho,gamma,b,Delta)
    l1_vec, v1_vec = solve_periode_1(w,h_vec,rho,beta,gamma,Delta,b,v2_interp)

    # b. figure
    fig1_1 = plt.figure()
    ax1 = fig1_1.add_subplot(1,1,1)
    ax1.plot(h2_vec_, v2(w,h2_vec_,1,rho,gamma,b), label = 'If $l_2=1$')
    plt.plot(h2_vec_, nowork_utility         , label = 'If $l_2=0$')
    ax1.plot(h_vec, l2_vec, label = 'Optimal utility')
    ax1.legend(loc='lower right')

    ax1.grid()
    ax1.set_xlabel('$h_2$')
    ax1.set_ylabel('Net Utility')
    ax1.set_title('Net-Utiliy function in period 2')
    ax1.set_xlim([0,1.7])
    ax1.set_ylim([-2,0])

    fig2_1 = plt.figure()
    ax2 = fig2_1.add_subplot(1,1,1)
    ax2.plot(h_vec, l1_vec, label = 'Optimal utility')

    ax2.grid()
    ax2.set_xlabel('$h_1$')
    ax2.set_ylabel('Net Utility')
    ax2.set_title('Net-Utiliy function in period 1');

widgets.interact(interactive_figure,
    w=widgets.FloatSlider(description="wage", min=0, max=3, step=0.1, value=2),
    b=widgets.FloatSlider(description="benefits", min=0, max=3, step=0.1, value=1),
    beta=widgets.FloatSlider(description="$beta$", min=0, max=3, step=0.1, value=0.96),
);
```

    interactive(children=(FloatSlider(value=2.0, description='wage', max=3.0), FloatSlider(value=0.96, description…

It is a little difficult to see it in periode 1 because we were unable to figure out how to show work and non work in that periode, however it is still possible to see the last kink in the curve where i gets rounded. That is where the agent starts working. So given high enough human capital.

# 2. AS-AD model

Consider the following **AS-AD model**. The **goods market equilibrium** is given by

$$ y_{t} = -\alpha r_{t} + v_{t} $$

where $y_{t}$ is the **output gap**, $r_{t}$ is the **ex ante real interest** and $v_{t}$ is a **demand disturbance**.

The central bank's **Taylor rule** is

$$ i_{t} = \pi_{t+1}^{e} + h \pi_{t} + b y_{t}$$

where $i_{t}$ is the **nominal interest rate**, $\pi_{t}$ is the **inflation gap**, and $\pi_{t+1}^{e}$ is the **expected inflation gap**.

The **ex ante real interest rate** is given by

$$ r_{t} = i_{t} - \pi_{t+1}^{e} $$

Together, the above implies that the **AD-curve** is

$$ \pi_{t} = \frac{1}{h\alpha}\left[v_{t} - (1+b\alpha)y_{t}\right]$$

Further, assume that the **short-run supply curve (SRAS)** is given by

$$ \pi_{t} = \pi_{t}^{e} + \gamma y_{t} + s_{t}$$

where $s_t$ is a **supply disturbance**.

**Inflation expectations are adaptive** and given by

$$ \pi_{t}^{e} = \phi\pi_{t-1}^{e} + (1-\phi)\pi_{t-1}$$

Together, this implies that the **SRAS-curve** can also be written as

$$ \pi_{t} = \pi_{t-1} + \gamma y_{t} - \phi\gamma y_{t-1} + s_{t} - \phi s_{t-1} $$

The **parameters** of the model are:

```python
par = {}

par['alpha'] = 5.76
par['h'] = 0.5
par['b'] = 0.5
par['phi'] = 0
par['gamma'] = 0.075
```

**Question 1:** Use the ``sympy`` module to solve for the equilibrium values of output, $y_t$, and inflation, $\pi_t$, (where AD = SRAS) given the parameters ($\alpha$, $h$, $b$, $\alpha$, $\gamma$) and $y_{t-1}$ , $\pi_{t-1}$, $v_t$, $s_t$, and $s_{t-1}$.

```python
#Defining relevant symbols
y, alpha, r, v, i, h, pi, b, gamma, phi, s, pi_lag, y_lag, s_lag, v_lag=sm.symbols('y alpha r v i h \\pi b gamma phi s \\pi_{t-1} y_{t-1} s_{t-1} v_{t-1}')
```

```python
# Question 1
#Define AD and SRAS curve as a sympy equations

AD_curve = sm.Eq(pi, 1/(h*alpha) * (v-(1+b*alpha)*y))
""" The function is the aggregate demand curve
    Arguments:
        h (float): Monetary policy parameter
        alpha (float): Real interest rate parameter
        v (int): Demand disturbance
        b (float): Monetary policy parameter
        y (float): Output gap

    Returns:
        pi (float): Inflation gap
    """
SRAS_curve = sm.Eq(pi, (pi_lag + gamma * y - phi * gamma * y_lag+s-phi * s_lag))

""" The function is the short run aggregate supply curve
    Arguments:
        pi_lag (int): Inflation gap lagged one period
        gamma (float ): Output parameter on the short run
        y (float): ...
        phi (int): Parameter forming inflation expectations
        y_lag (int): Output gap lagged one period
        s (int): Supply disturbance
        s_lag (int) : Supply disturbance lagged one period

    Returns:
        pi (float): ...
        """
```

    ' The function is the short run aggregate supply curve\n    Arguments:\n        pi_lag (int): Inflation gap lagged one period\n        gamma (float ): Output parameter on the short run\n        y (float): ...\n        phi (int): Parameter forming inflation expectations\n        y_lag (int): Output gap lagged one period\n        s (int): Supply disturbance\n        s_lag (int) : Supply disturbance lagged one period\n    \n    Returns: \n        pi (float): ...\n        '

```python
# Solving the equilibrium for y and pi given the parameters with sympy solve
sol_dict = sm.solve((AD_curve, SRAS_curve), (y, pi))

# Save the equilibrium values in a list
result_list = list(sol_dict.values())

#Saving the equilibrium as two variables
y_sol=result_list[1]
pi_sol = result_list[0]
```

The equilibrium values of $y_t$ and $\pi_t$ given the paramerters $\alpha, h, b, \phi$ and $\gamma$ and $y_{t-1}, \pi_{t-1}, v_t, s_t$ and $s_{t-1}$  is presented below.

```python
#Present the answer in latex format
display(
    Markdown(
        r'In equilibrium $y_t$ is: $ y_t= $ {}'.format(
            sm.latex(y_sol, mode='inline'))))
display(
    Markdown(
        r'In equilibrium $\pi_t$ is: $ \pi_t= $ {}'.format(
            sm.latex(pi_sol, mode='inline'))))
```

In equilibrium $y_t$ is: $ y_t= $ $\frac{- \pi_{t-1} \alpha h + \alpha \gamma h \phi y_{t-1} + \alpha h \phi s_{t-1} - \alpha h s + v}{\alpha b + \alpha \gamma h + 1}$

In equilibrium $\pi_t$ is: $ \pi_t= $ $\frac{\gamma v + \left(\alpha b + 1\right) \left(\pi_{t-1} - \gamma \phi y_{t-1} - \phi s_{t-1} + s\right)}{\alpha b + \alpha \gamma h + 1}$

**Question 2:** Find and illustrate the equilibrium when $y_{t-1} = \pi_{t-1} = v_t = s_t = s_{t-1} = 0$. Illustrate how the equilibrium changes when instead $v_t = 0.1$.

```python
#Calculating the values of y and pi in the equilibrium given the parameter values
#First, turn the solutions into lambda functions using sympy lambdify. Next insert the parameter and variable values as stated.

    # For y:
y_lamb = sm.lambdify((alpha, h, phi, s_lag, y_lag, pi_lag, s, v, b, gamma), y_sol)
y_numb = y_lamb(5.76, 0.5, 0, 0, 0, 0, 0, 0, 0.5, 0.075)

    #For pi:
pi_lamb = sm.lambdify((v, alpha, b, gamma, h, phi, s_lag, y_lag, pi_lag, s), pi_sol)
pi_numb = pi_lamb(0, 5.76, 0.5, 0.075, 0.5, 0, 0, 0, 0, 0)
```

Given the parameter and variable values the equilibrium values of $y_t$ and $\pi_t$ is presented below.

```python
#Presenting the result
display(
    Markdown(
        r'The  value of $y_t$ is $ y_t= $ {}'.format(
            sm.latex(y_numb, mode='inline'))))
display(
    Markdown(
        r'The  value of $\pi_t$ is $ \pi_t= $ {}'.format(
            sm.latex(pi_numb, mode='inline'))))
```

The  value of $y_t$ is $ y_t= $ $0.0$

The  value of $\pi_t$ is $ \pi_t= $ $0.0$

```python
# A demand disturbance is presented: v_t=0.1
# Calculating the equilibrium values of y and pi when v=0.1

y_numb2=y_lamb(5.76, 0.5, 0, 0, 0, 0, 0, 0.1, 0.5, 0.075)
pi_numb2=pi_lamb(0.1, 5.76, 0.5, 0.075, 0.5, 0, 0, 0, 0, 0)
```

Given a demand disturbance where $v_t=0.1$ the new equilibrium values of $y_t$ and $\pi_t$ is presented below.

```python
#Presenting the result
display(
    Markdown(
        r'The  value of $y_t$ is then $ y_t= $ {}'.format(
            sm.latex(y_numb2, mode='inline'))))
display(
    Markdown(
        r'The  value of $\pi_t$ is then $ \pi_t= $ {}'.format(
            sm.latex(pi_numb2, mode='inline'))))
```

The  value of $y_t$ is then $ y_t= $ $0.0244140625$

The  value of $\pi_t$ is then $ \pi_t= $ $0.0018310546875$

```python
# Illustrate the equilibrium

#Define AD and AS as functions functions
def AD(y):
    return 1/(h*alpha) * (v-(1+b*alpha)*y)

def SRAS(y):
    return pi_lag + gamma * y - phi * gamma * y_lag + s-phi * s_lag

def AD2(y):
    return 1/(h*alpha) * (v2-(1+b*alpha)*y)
```

```python
# Set parameter values as stated in the question
h=0.5
alpha = 5.76
v=0
v2=0.1
b=0.5
pi_lag=0
gamma = 0.075
phi=0
y_lag=0
s=0
s_lag=0
v_lag=0
```

```python
#Plot figure of the AD SRAS model
x_ax=np.linspace(-1,1,10)
y=AD(x_ax)
z=SRAS(x_ax)
w=AD2(x_ax)

plt.figure(figsize=(15,8))
plt.plot(x_ax,y,"r",label="AD curve")
plt.plot(x_ax, z, "green", label="SRAS curve")
plt.plot(x_ax, w, "orange", label="AD curve w demand schock")
plt.plot(y_numb2, pi_numb2, color='black', marker='o')
plt.plot(y_numb, pi_numb, color='black', marker='o')
plt.legend()
plt.xlim(-0.2,0.2)
plt.ylim(-0.2,0.2)
plt.xlabel('Output, y')
plt.ylabel('Inflation, $\pi_t$')
plt.title('AS-AD diagram')
plt.grid(True)

plt.show()
```

The illustration states that the AD curves are downward sloping and the AS curve is upwards sloping making a cross.
The two black dots are illustrating the equilibria before and after the demand disturbance, $v_t$. The shock shifts the AD curve to the right implying that both output and inflation increases.

**Persistent disturbances:** Now, additionaly, assume that both the demand and the supply disturbances are AR(1) processes

$$ v_{t} = \delta v_{t-1} + x_{t} $$
$$ s_{t} = \omega s_{t-1} + c_{t} $$

where $x_{t}$ is a **demand shock**, and $c_t$ is a **supply shock**. The **autoregressive parameters** are:

```python
par['delta'] = 0.80
par['omega'] = 0.15
```

**Question 3:** Starting from $y_{-1} = \pi_{-1} = s_{-1} = 0$, how does the economy evolve for $x_0 = 0.1$, $x_t = 0, \forall t > 0$ and $c_t = 0, \forall t \geq 0$?

```python
#Define new symbols relevant for the question
sigma_x, sigma_c, delta, omega, x, c = sm.symbols('sigma_x sigma_c delta omega x c')
```

```python
# It is assumed that the demand and supply disturbances are AR(1) proccesses. We define them as functions below.

def v_func(v_lag, x):
    """ This function is an AR(1) demand disturbance
        Arguments:
            v_lag (int): Demand disturbance lagged one period
            x (float): Demand shock
            delta (float): Autoregressive parameter

        Returns:
            v_func (function): Function of demand disturbance
    """
    return delta * v_lag + x

def s_func(s_lag, c):
    """ This function is an AR(1) supply disturbance
        Arguments:
            s_lag (type): Supply disturbance lagged one period
            c (type): Supply shock
            omega (float): Autoregressive parameter

        Returns:
            c_func (function): Function fo supply disturbance
    """
    return omega * s_lag + c
```

```python
# Set parameter values for delta and omega
delta = 0.8
omega = 0.15

# Set a random seed
seed = 2030
np.random.seed(seed)

# Simulating for 100 periods
T = 100

# Creating empty vectors to append to simulation in the loop
y_vec = [0]
pi_vec = [0]
v_vec = [0]
s_vec = [0]
x_vec=np.zeros(T)
c_vec=np.zeros(T)
x_vec[1]=0.1

# The model is simulated in a range of 100 periods.
# The loop appends the result of every new period to the empty vectors created above.
for t in range(1,T):

    v_vec.append(v_func(v_vec[t-1], x_vec[t]))
    s_vec.append(s_func(s_vec[t-1], c_vec[t]))

    y_vec.append(y_lamb(alpha, h, phi, s_vec[t-1], y_vec[t-1], pi_vec[t-1], s_vec[t], v_vec[t], b, gamma))
    pi_vec.append(pi_lamb(v_vec[t], alpha, b, gamma, h, phi, s_vec[t-1], y_vec[t-1], pi_vec[t-1], s_vec[t]))
```

```python
# Plot showing the evolution of y and pi with a demand shock v
time = np.linspace(0,T,T)
plt.figure(figsize=(10,8))
plt.plot(time,y_vec, "blue", label='$y_t$')
plt.plot(time, pi_vec, "orange", label='$\pi_t$')
plt.xlabel('Time, t')
plt.ylabel('$y_t$ and $\pi_t$')
plt.title('The evolution of the economy at time t')
plt.grid()
plt.legend()
plt.show()
```

As the demand shock hits in period 1 the output increases immediately whereafter it decreases and becomes negative. The inflation increases for a smaller period. Both curves converges to zero in the long run. As the graph shows it takes circa 100 periods for the economy to stabilize.

**Stochastic shocks:** Now, additionally, assume that $x_t$ and $c_t$ are stochastic and normally distributed

$$ x_{t}\sim\mathcal{N}(0,\sigma_{x}^{2}) $$
$$ c_{t}\sim\mathcal{N}(0,\sigma_{c}^{2}) $$

The **standard deviations of the shocks** are:

```python
par['delta'] = 0.80
par['omega'] = 0.15
```

**Question 4:** Simulate the AS-AD model for 1,000 periods. Calculate the following five statistics:

1. Variance of $y_t$, $var(y_t)$
2. Variance of $\pi_t$, $var(\pi_t)$
3. Correlation between $y_t$ and $\pi_t$, $corr(y_t,\pi_t)$
4. Auto-correlation between $y_t$ and $y_{t-1}$, $corr(y_t,y_{t-1})$
5. Auto-correlation between $\pi_t$ and $\pi_{t-1}$, $corr(\pi_t,\pi_{t-1})$

```python
# Simulation of the AS-AD model
# The values of the standard deviations of the shocks is stated
sigma_x=3.492
sigma_c=0.2

# Setting a random seed
np.random.seed(seed)

# The model is simulated for a thousand periods.
T = 1000

# Creating empty vectors for the variables of the model
y_vec2 = [0]
pi_vec2 = [0]
v_vec2 = [0]
s_vec2 = [0]

# Creating stochastic shocks as standard normally distributed
x_vec2 = np.random.normal(loc=0,scale=sigma_x,size=T)
c_vec2 = np.random.normal(loc=0,scale=sigma_c,size=T)

# The loop simulates the model by appending to the empty vectors every value of the functions in the thousand periods.
for t in range(1,T):
    v_vec2.append(v_func(v_vec2[t-1], x_vec2[t]))
    s_vec2.append(s_func(s_vec2[t-1], c_vec2[t]))

    y_vec2.append(y_lamb(alpha, h, phi, s_vec2[t-1], y_vec2[t-1], pi_vec2[t-1], s_vec2[t], v_vec2[t], b, gamma))
    pi_vec2.append(pi_lamb(v_vec2[t], alpha, b, gamma, h, phi, s_vec2[t-1], y_vec2[t-1], pi_vec2[t-1], s_vec2[t]))
```

```python
# Plot the simulation
periods = np.linspace(0,T,T)
plt.figure(figsize=(12,6))

# Subplot for inflation

plt.subplot(2,1,1)
plt.plot(periods, pi_vec2, color='blue', label='Inflation')
plt.title('Simulation of the AD - AS model')
plt.ylabel('Inflation, $\pi_t$')
plt.grid()
plt.legend()

# Subplot for output
plt.subplot(2,1,2)
plt.plot(periods, y_vec2, color='orange', label='Output')
plt.ylabel('Output, $y_t$')
plt.xlabel('Periods')
plt.grid()
plt.legend()

plt.show()
```

The figure shows a simulation of the AS AD model with stochastic demand and supply shocks. The model is simulated over 1000 periods and showing both high as well as low levels of y and $\pi$.

```python
# Calculating 5 statistics
#Variance of y
var_y=np.var(y_vec2)

#Variance of pi
var_pi=np.var(pi_vec2)

#Correlation between y and pi
corr=np.corrcoef(y_vec2, pi_vec2)

# Autocorrelation between y and y_lag
auto_corr_y=np.corrcoef(y_vec2[1:],y_vec2[:-1])

#Autocorrelation between pi and pi_lag
auto_corr_pi=np.corrcoef(pi_vec2[1:], pi_vec2[:-1])

```

```python
print(f'The variance of y is: {var_y:.3f}')
print(f'The variance of pi is: {var_pi:.3f}')
print(f'The correlation between y and pi is: {corr[0,1]:.3f}')
print(f'The autocorrelation of y is: {auto_corr_y[0,1]:.3f}')
print(f'The autocorrelation of pi is: {auto_corr_pi[0,1]:.3f}')
```

    The variance of y is: 1.930
    The variance of pi is: 1.244
    The correlation between y and pi is: -0.274
    The autocorrelation of y is: 0.767
    The autocorrelation of pi is: 0.981

**Question 5:** Plot how the correlation between $y_t$ and $\pi_t$ changes with $\phi$. Use a numerical optimizer or root finder to choose $\phi\in(0,1)$ such that the simulated correlation between $y_t$ and $\pi_t$ comes close to 0.31.

```python
#Setting a random seed
np.random.seed(seed)

#Simulate for thousand periods
T = 1000

# Creating stochastic shocks as standard normally distributed
x_vec3 = np.random.normal(loc=0,scale=sigma_x,size=T)
c_vec3 = np.random.normal(loc=0,scale=sigma_c,size=T)

# Simulation of the model with phi as the changing parameter.
def simulation(phi):
    y_vec3 = [0]
    pi_vec3 = [0]
    v_vec3 = [0]
    s_vec3 = [0]
    corr_ypi = [0]

    for t in range(1,T):
        v_vec3.append(v_func(v_vec3[t-1], x_vec3[t]))
        s_vec3.append(s_func(s_vec3[t-1], c_vec3[t]))

        y_vec3.append(y_lamb(alpha, h, phi, s_vec3[t-1], y_vec3[t-1], pi_vec3[t-1], s_vec3[t], v_vec3[t], b, gamma))
        pi_vec3.append(pi_lamb(v_vec3[t], alpha, b, gamma, h, phi, s_vec3[t-1], y_vec3[t-1], pi_vec3[t-1], s_vec3[t]))

        corr_ypi = np.corrcoef(y_vec3, pi_vec3)[1,0]

    return y_vec3, pi_vec3, corr_ypi

#np.corrcoef(y_vec3, pi_vec3)[1,0]
#y_vec3, pi_vec3, corr_ypi

y_vec3, pi_vec3, corr_ypi= simulation(phi)
```

```python
#Find optimal phi in simulation
# Creating a lambda function of phi
phi_func = lambda phi: np.corrcoef(simulation(phi)[0], simulation(phi)[1])[1,0] - 0.3100

# Optimizing the phi function applying the Brent method
result = optimize.root_scalar(phi_func, bracket = [0,1], method = 'brentq')

optimal_phi = result.root

print(f' The optimal level of phi is: {optimal_phi:.3f}')
```

     The optimal level of phi is: 0.985

```python
# Simulation of the model to use for plotting
plot = np.linspace(0,1,T) #phi_plot
plot_correlation = []  #corr_plot

for x in plot:
    y_vec3, pi_vec3, corr_ypi = simulation(x)
    plot_correlation.append(corr_ypi)
```

```python
# Figure of the correlation between y and pi with changing phi
plt.figure(figsize=(12,6))
plt.plot(plot, plot_correlation, color='green')
plt.plot(optimal_phi, 0.31, marker='o')
plt.xlabel('$\phi$')
plt.ylabel('Correlation between $y_t$ and $\pi_t$')
plt.title('Correlation')
plt.axhline(0.31, linestyle='dashed', color='red')
plt.axvline(optimal_phi, linestyle='dashed', color='red')
plt.grid()
plt.show()
```

The figure illustrates the correlation between $y_t$ and $\pi_t$ as $\phi$ changes between 0 and 1.
The red dotted lines show that the when the correlation is as close to 0.31 as possible when the optimal value of phi is 0.985.

**Quesiton 6:** Use a numerical optimizer to choose $\sigma_x>0$, $\sigma_c>0$ and $\phi\in(0,1)$ to make the simulated statistics as close as possible to US business cycle data where:

1. $var(y_t) = 1.64$
2. $var(\pi_t) = 0.21$
3. $corr(y_t,\pi_t) = 0.31$
4. $corr(y_t,y_{t-1}) = 0.84$
5. $corr(\pi_t,\pi_{t-1}) = 0.48$

# 3. Exchange economy

Consider an **exchange economy** with

1. 3 goods, $(x_1,x_2,x_3)$
2. $N$ consumers indexed by \\( j \in \{1,2,\dots,N\} \\)
3. Preferences are Cobb-Douglas with log-normally distributed coefficients

    $$ \begin{eqnarray*}
    u^{j}(x_{1},x_{2},x_{3}) &=&
    \left(x_{1}^{\beta_{1}^{j}}x_{2}^{\beta_{2}^{j}}x_{3}^{\beta_{3}^{j}}\right)^{\gamma}\\
     &  & \,\,\,\beta_{i}^{j}=\frac{\alpha_{i}^{j}}{\alpha_{1}^{j}+\alpha_{2}^{j}+\alpha_{3}^{j}} \\
     &  & \,\,\,\boldsymbol{\alpha}^{j}=(\alpha_{1}^{j},\alpha_{2}^{j},\alpha_{3}^{j}) \\
     &  & \,\,\,\log(\boldsymbol{\alpha}^j) \sim \mathcal{N}(\mu,\Sigma) \\
    \end{eqnarray*} $$

4. Endowments are exponentially distributed,

$$
\begin{eqnarray*}
\boldsymbol{e}^{j} &=& (e_{1}^{j},e_{2}^{j},e_{3}^{j}) \\
 &  & e_i^j \sim f, f(z;\zeta) =  1/\zeta \exp(-z/\zeta)
\end{eqnarray*}
$$

Let $p_3 = 1$ be the **numeraire**. The implied **demand functions** are:

$$
\begin{eqnarray*}
x_{i}^{\star j}(p_{1},p_{2},\boldsymbol{e}^{j})&=&\beta^{j}_i\frac{I^j}{p_{i}} \\
\end{eqnarray*}
$$

where consumer $j$'s income is

$$I^j = p_1 e_1^j + p_2 e_2^j +p_3 e_3^j$$

The **parameters** and **random preferences and endowments** are given by:

```python
# a. parameters
N = 50000
mu = np.array([3,2,1])
Sigma = np.array([[0.25, 0, 0], [0, 0.25, 0], [0, 0, 0.25]])
gamma = 0.8
zeta = 1

# b. random draws
seed = 1986
np.random.seed(seed)

# preferences
    #draw alphas
alphas = np.exp(np.random.multivariate_normal(mu, Sigma, size=N))

#draw betas
betas = alphas/np.reshape(np.sum(alphas,axis=1),(N,1))

# endowments
e1 = np.random.exponential(zeta,size=N)
e2 = np.random.exponential(zeta,size=N)
e3 = np.random.exponential(zeta,size=N)
```

**Question 1:** Plot the histograms of the budget shares for each good across agents.

```python
## plot histogram of budgetshares of the three goods
fig = plt.figure(dpi=100)
ax = fig.add_subplot(1,1,1)
ax.hist(betas,bins=90,density=True,label=['Good 1', 'Good 2', 'Good 3'])
ax.set_title('The budget shares of three goods')
ax.set_xlabel('betas')
ax.set_ylabel('Consumers')
ax.legend(loc='upper right');
```

The budget shares are given by the fraction of demand of each good:

$$
\begin{eqnarray*}
\frac{x_{i}^{\star j}(p_{1},p_{2},\boldsymbol{e}^{j})}{I}&=& \frac{\beta^{j}_i\frac{I^j}{p_{i}}}{I} &=& \frac{\beta^{j}_i}{p_{i}}  \\
\end{eqnarray*}
$$

The y-axis indicate consumers pr thousands, and the beta tells us the share of their budget they use on each good. As can be seen from the histogram consumers use the moste of their budget on good 1. Then good 2 and lastly good 3.

Consider the **excess demand functions:**

$$ z_i(p_1,p_2) = \sum_{j=1}^N x_{i}^{\star j}(p_{1},p_{2},\boldsymbol{e}^{j}) - e_i^j$$

**Question 2:** Plot the excess demand functions.

```python
## Slicing the arrays
print(betas)
```

    [[0.53104511 0.30693723 0.16201766]
     [0.50866997 0.4162364  0.07509363]
     [0.83317207 0.07722226 0.08960567]
     ...
     [0.63725404 0.22027742 0.14246854]
     [0.78205036 0.11988119 0.09806844]
     [0.65552751 0.24728801 0.09718448]]

```python
## We want to slice the arrays so we can define the demand of each good
## taking all rows (:) but keeping the first column (1) and so on

beta_g1 = betas[:,0]
beta_g1
```

    array([0.53104511, 0.50866997, 0.83317207, ..., 0.63725404, 0.78205036,
           0.65552751])

```python
beta_g3 = betas[:,2]
beta_g3
```

    array([0.16201766, 0.07509363, 0.08960567, ..., 0.14246854, 0.09806844,
           0.09718448])

```python
## The method used is greatly inspired from problemset 2 and 7 and lecture 3 and 4

# a. Define demandfunctions

def demand_g1(betas,e1,e2,e3,p1,p2):
    '''
    calculates the demand of good 1

    Args:
        p1 (float): Price for good 1
        p2 (float): Price for good 2
        e1 (numpy array): Endowments of good 1
        e2 (numpy array): Endowments of good 2
        e3 (numpy array): Endowments of good 3
        betas (type: numpy ndarray): Preferences for goods

    Return: Demand of good 1. (It is the same arguments for all goods)
    '''
    I = p1*e1+p2*e2+e3
    return betas[:,0]*I/p1

def demand_g2(betas,e1,e2,e3,p1,p2):
    I = p1*e1+p2*e2+e3
    return betas[:,1]*I/p2

# As p3 = 1 we do not need to specify the demand of good 3
```

```python
# b. Calculate excess demand
# excess demand good 1
def excess_demand_g1(betas, p1, p2, e1, e2, e3):
    """
    Calculating excess demand of good 1

    Returns:
        excess (float): Excess demand of good 1
    """
    # i. sum array of demand
    demand = np.sum(demand_g1(betas,e1,e2,e3,p1,p2))

    # ii. sum array of supply
    supply = np.sum(e1)

    # iii. Calculate excess demand
    excess_demand = demand - supply

    return excess_demand

# excess demand good 2
def excess_demand_g2(betas, p1, p2, e1, e2, e3):

    # i. sum array of demand
    demand = np.sum(demand_g2(betas,e1,e2,e3,p1,p2))

    # ii. sum array of supply
    supply = np.sum(e2)

     # iii. Calculate excess demand
    excess_demand = demand - supply

    return excess_demand
```

```python
# to illustrate the excess demand function we need to specify pricevectors of p1
# and p2. We can then fill the numpy arrays (grids) of the excess demand functions.

# a. choosing number of values and maximum price
p_max = 10
seize = 100
shape_tuple = (seize,seize)

# b. Generating arrays of prices
p1_vec = np.linspace(0.1,p_max,seize)
p2_vec = np.linspace(0.1,p_max,seize)

# c. allocate numpy arrays
excess1_grid = np.empty(shape_tuple)
excess2_grid = np.empty(shape_tuple)

# d. Transforming price-vectors to grids
p1_grid,p2_grid = np.meshgrid(p1_vec,p2_vec,indexing='ij')

for i,p1 in enumerate(p1_vec):
    for j,p2 in enumerate(p2_vec):
        excess1_grid[i,j] = excess_demand_g1(betas, p1, p2, e1, e2, e3)
        excess2_grid[i,j] = excess_demand_g2(betas, p1, p2, e1, e2, e3)
```

```python
fig = plt.figure() # create the figure
ax = fig.add_subplot(1,1,1,projection='3d') # create a 3d axis in the figure
ax.plot_surface(p1_grid,p2_grid,excess1_grid)
ax.set_title('Excess demand good 1')
ax.set_xlabel('$p_1$')
ax.set_ylabel('$p_2$')

fig = plt.figure() # create the figure
ax1 = fig.add_subplot(1,1,1,projection='3d') # create a 3d axis in the figure
ax1.plot_surface(p1_grid,p2_grid,excess2_grid)
ax1.set_title('Excess demand good 2')
ax1.set_xlabel('$p_1$')
ax1.set_ylabel('$p_2$')

;
```

**Quesiton 3:** Find the Walras-equilibrium prices, $(p_1,p_2)$, where both excess demands are (approximately) zero, e.g. by using the following tâtonnement process:

1. Guess on $p_1 > 0$, $p_2 > 0$ and choose tolerance $\epsilon > 0$ and adjustment aggressivity parameter, $\kappa > 0$.
2. Calculate $z_1(p_1,p_2)$ and $z_2(p_1,p_2)$.
3. If $|z_1| < \epsilon$ and $|z_2| < \epsilon$ then stop.
4. Else set $p_1 = p_1 + \kappa \frac{z_1}{N}$ and $p_2 = p_2 + \kappa \frac{z_2}{N}$ and return to step 2.

```python

# equilibrium function
def find_equilibrium(betas,p1,p2,p3,e1,e2,e3,kappa=0.5,eps=1e-8,maxiter=50000):

    t = 1
    while True:

        # a. step 1: excess demand
        Z1 = excess_demand_g1(betas, p1, p2, e1, e2, e3)
        Z2 = excess_demand_g2(betas, p1, p2, e1, e2, e3)

        # b. step 2: stop?
        if (np.abs(Z1) < eps or np.abs(Z2) < eps) or t >= maxiter:
            print(f'{t:3d}: p1 = {p1:12.8f} -> excess demand -> {Z1:14.8f}')
            print(f'{t:3d}: p2 = {p2:12.8f} -> excess demand -> {Z2:14.8f}')
            break

        # c. step 3: update p1 and p2
        p1 = p1 + kappa*Z1/N
        p2 = p2 + kappa*Z2/N

        # d. step 4: return
        if t < 5 or t%2500 == 0:
            print(f'{t:3d}: p1 = {p1:12.8f} -> excess demand -> {Z1:14.8f}')
            print(f'{t:3d}: p2 = {p2:12.8f} -> excess demand -> {Z2:14.8f}')
        elif t == 5:
            print ('    ...')

        t += 1 # Updating iteration counter

    return p1, p2
```

```python
# find equilibrium function
p1 = 5
p2 = 2
p3 = 1
kappa = 0.1
eps = 1e-8
p1,p2 = find_equilibrium(betas,p1,p2,p3,e1,e2,e3,kappa=kappa,eps=eps)
```

      1: p1 =   5.00275451 -> excess demand ->  1377.25348781
      1: p2 =   2.00357081 -> excess demand ->  1785.40484742
      2: p1 =   5.00553344 -> excess demand ->  1389.46580224
      2: p2 =   2.00703918 -> excess demand ->  1734.18452208
      3: p1 =   5.00833527 -> excess demand ->  1400.91730723
      3: p2 =   2.01040972 -> excess demand ->  1685.27131126
      4: p1 =   5.01115856 -> excess demand ->  1411.64436855
      4: p2 =   2.01368682 -> excess demand ->  1638.54765189
        ...
    2500: p1 =   6.47759971 -> excess demand ->    11.41316878
    2500: p2 =   2.61204736 -> excess demand ->     4.25784940
    5000: p1 =   6.48992621 -> excess demand ->     0.11792691
    5000: p2 =   2.61664590 -> excess demand ->     0.04399355
    7500: p1 =   6.49005381 -> excess demand ->     0.00122304
    7500: p2 =   2.61669350 -> excess demand ->     0.00045627
    10000: p1 =   6.49005513 -> excess demand ->     0.00001268
    10000: p2 =   2.61669400 -> excess demand ->     0.00000473
    12500: p1 =   6.49005515 -> excess demand ->     0.00000013
    12500: p2 =   2.61669400 -> excess demand ->     0.00000005
    13372: p1 =   6.49005515 -> excess demand ->     0.00000003
    13372: p2 =   2.61669400 -> excess demand ->     0.00000001

We thus get that the Walras-equilibrium price for good 1 is 6.49 and good 2 is 2.61

**Question 4:** Plot the distribution of utility in the Walras-equilibrium and calculate its mean and variance.

To do that we will first define the utility function. Then we can calculate the utility for all consumers, goods and with newly found prices. We can then plot the distribution of the utility.

```python
#Deine utility function
def utility(betas, p1, p2, e1, e2, e3,gamma):
    I = p1*e1+p2*e2+e3
    x1 = betas[:,0]*(I/p1)
    x2 = betas[:,1]*(I/p2)
    x3 = betas[:,2]*I

    return (x1**betas[:,0]*x2**betas[:,1]*x3**betas[:,2])**gamma
```

```python
u_vec = utility(betas,p1, p2, e1, e2, e3, gamma)

plt.hist(u_vec,bins =500) #every bin represents 1000 consumers

plt.xlabel('Utility')
plt.ylabel('Consumers')
plt.title('Utilities Distribution')
```

```python
## We can now caluculate the mean and variance.
mean = np.mean(u_vec)
variance = np.var(u_vec)

mean,variance
```

    (1.0101644598643857, 0.31734020500163673)

**Question 5:** Find the Walras-equilibrium prices if instead all endowments were distributed equally. Discuss the implied changes in the distribution of utility. Does the value of $\gamma$ play a role for your conclusions?

```python
#The approch is the same, but with a new e.
e = e1
```

```python
## The method used is greatly inspired from problemset 2 and 7 and lecture 3 and 4

# a. Define demandfunctions

def demand_g1_new(betas,e,p1_new,p2_new):

    I = p1_new*e+p2_new*e+e
    return betas[:,0]*I/p1_new

def demand_g2_new(betas,e,p1_new,p2_new):

    I = p1_new*e+p2_new*e+e
    return betas[:,1]*I/p2_new

# As p3 = 1 we do not need to specify the demand of good 3
```

```python
# b. Calculate excess demand
# excess demand good 1
def excess_demand_g1_new(betas,e,p1_new,p2_new):
    """
    Calculating excess demand of good 1

    Returns:
        excess (float): Excess demand of good 1
    """
    # i. sum array of demand
    demand = np.sum(demand_g1_new(betas,e,p1_new,p2_new))

    # ii. sum array of supply
    supply = np.sum(e)

    # iii. Calculate excess demand
    excess_demand = demand - supply

    return excess_demand

# excess demand good 2
def excess_demand_g2_new(betas,e,p1_new,p2_new):

    # i. sum array of demand
    demand = np.sum(demand_g2_new(betas,e,p1_new,p2_new))

    # ii. sum array of supply
    supply = np.sum(e)

     # iii. Calculate excess demand
    excess_demand = demand - supply

    return excess_demand
```

```python

# equilibrium function
def find_equilibrium_new(betas,p1_new,p2_new,p3_new,e,kappa=0.5,eps=1e-8,maxiter=50000):

    t = 0
    while True:

        # a. step 1: excess demand
        Z1 = excess_demand_g1_new(betas,e,p1_new,p2_new)
        Z2 = excess_demand_g2_new(betas,e,p1_new,p2_new)

        # b. step 2: stop?
        if (np.abs(Z1) < eps or np.abs(Z2) < eps) or t >= maxiter:
            print(f'{t:3d}: p1_new = {p1:12.8f} -> excess demand -> {Z1:14.8f}')
            print(f'{t:3d}: p2_new = {p2:12.8f} -> excess demand -> {Z2:14.8f}')
            break

        # c. step 3: update p1 and p2
        p1_new = p1_new + kappa*Z1/N
        p2_new = p2_new + kappa*Z2/N

        # d. step 4: return
        if t < 5 or t%2500 == 0:
            print(f'{t:3d}: p1_new = {p1:12.8f} -> excess demand -> {Z1:14.8f}')
            print(f'{t:3d}: p2_new = {p2:12.8f} -> excess demand -> {Z2:14.8f}')
        elif t == 5:
            print ('    ...')

        t += 1 # Updating iteration counter

    return p1_new, p2_new
```

```python
# find equilibrium function
p1_new = 5
p2_new = 2
p3_new = 1
kappa = 0.5
eps = 1e-8
p1_new,p2_new = find_equilibrium_new(betas,p1_new,p2_new,p3_new,e,kappa=0.5,eps=1e-8,maxiter=50000)
```

      0: p1_new =   6.49000000 -> excess demand -> 98641966.82162084
      0: p2_new =   2.61700000 -> excess demand -> 75732668.40843812
      1: p1_new =   6.49000000 -> excess demand -> 101167836.37735206
      1: p2_new =   2.61700000 -> excess demand -> 53035817.46182784
      2: p1_new =   6.49000000 -> excess demand -> 94134619.03151079
      2: p2_new =   2.61700000 -> excess demand -> 58723633.64492429
      3: p1_new =   6.49000000 -> excess demand -> 93753517.80718455
      3: p2_new =   2.61700000 -> excess demand -> 59081961.66602881
      4: p1_new =   6.49000000 -> excess demand -> 93645553.44944049
      4: p2_new =   2.61700000 -> excess demand -> 59181379.87152798
        ...
    2500: p1_new =   6.49000000 -> excess demand -> 93519675.86399658
    2500: p2_new =   2.61700000 -> excess demand -> 59282896.44600989
    5000: p1_new =   6.49000000 -> excess demand -> 93519663.44944806
    5000: p2_new =   2.61700000 -> excess demand -> 59282888.86201406
    7500: p1_new =   6.49000000 -> excess demand -> 93519659.33700047
    7500: p2_new =   2.61700000 -> excess demand -> 59282886.30799633
    10000: p1_new =   6.49000000 -> excess demand -> 93519657.28560092
    10000: p2_new =   2.61700000 -> excess demand -> 59282885.02610988
    12500: p1_new =   6.49000000 -> excess demand -> 93519656.05630459
    12500: p2_new =   2.61700000 -> excess demand -> 59282884.25541750
    15000: p1_new =   6.49000000 -> excess demand -> 93519655.23741727
    15000: p2_new =   2.61700000 -> excess demand -> 59282883.74097189
    17500: p1_new =   6.49000000 -> excess demand -> 93519654.65281247
    17500: p2_new =   2.61700000 -> excess demand -> 59282883.37319253
    20000: p1_new =   6.49000000 -> excess demand -> 93519654.21453121
    20000: p2_new =   2.61700000 -> excess demand -> 59282883.09718374
    22500: p1_new =   6.49000000 -> excess demand -> 93519653.87374768
    22500: p2_new =   2.61700000 -> excess demand -> 59282882.88240725
    25000: p1_new =   6.49000000 -> excess demand -> 93519653.60118535
    25000: p2_new =   2.61700000 -> excess demand -> 59282882.71052083
    27500: p1_new =   6.49000000 -> excess demand -> 93519653.37822227
    27500: p2_new =   2.61700000 -> excess demand -> 59282882.56984354
    30000: p1_new =   6.49000000 -> excess demand -> 93519653.19244890
    30000: p2_new =   2.61700000 -> excess demand -> 59282882.45258294
    32500: p1_new =   6.49000000 -> excess demand -> 93519653.03527689
    32500: p2_new =   2.61700000 -> excess demand -> 59282882.35334138
    35000: p1_new =   6.49000000 -> excess demand -> 93519652.90057306
    35000: p2_new =   2.61700000 -> excess demand -> 59282882.26826198
    37500: p1_new =   6.49000000 -> excess demand -> 93519652.78384110
    37500: p2_new =   2.61700000 -> excess demand -> 59282882.19451497
    40000: p1_new =   6.49000000 -> excess demand -> 93519652.68170929
    40000: p2_new =   2.61700000 -> excess demand -> 59282882.12997764
    42500: p1_new =   6.49000000 -> excess demand -> 93519652.59159958
    42500: p2_new =   2.61700000 -> excess demand -> 59282882.07302627
    45000: p1_new =   6.49000000 -> excess demand -> 93519652.51150730
    45000: p2_new =   2.61700000 -> excess demand -> 59282882.02239748
    47500: p1_new =   6.49000000 -> excess demand -> 93519652.43985005
    47500: p2_new =   2.61700000 -> excess demand -> 59282881.97709379
    50000: p1_new =   6.49000000 -> excess demand -> 93519652.37536184
    50000: p2_new =   2.61700000 -> excess demand -> 59282881.93631705

So we think that we have the right approach by defining a e that such all have the same endowment, but somthing is going wrong and  we can not find the error.


```python
from Arbie import Token
from Arbie.Actions.path_finder import TradingGraph
from Arbie.Variables.pool import Pool
import networkx as nx

eth = Token('eth', 1)  # 300
dai = Token('dai', 300)  # 1
btc = Token('btc', 3.0/100)  # 10000
yam = Token('yam', 3000)  # 0.1

small = 10000
medium = 1000000
large = 100000000

pools = [
        Pool(
            [eth, dai, yam],
            [small / 303.0, small / 0.9, small / 0.1],
            [1 / 3.0, 1 / 3.0, 1 / 3.0], 0.004),
        Pool(
            [eth, btc],
            [large / 305.0, large / 10000],
            [5 / 6, 1 / 6], 0.01),
        Pool(
            [eth, dai, btc],
            [medium / 301.0, medium / 1.1, medium / 10020],
            [1 / 2.0, 1 / 4.0, 1 / 4.0], 0.004),
        Pool(
            [dai, yam],
            [small / 1.1, small / 0.1],
            [1 / 2.0, 1 / 2.0], 0.001),
    ]

graph = TradingGraph(pools)
graph.graph
```

    <networkx.classes.multidigraph.MultiDiGraph at 0x7f8f9a7cceb0>

```python
import matplotlib.pyplot as plt
pos = nx.random_layout(graph.graph)
nx.draw_networkx_nodes(graph.graph, pos, node_size = 600, alpha = 1)
labels = nx.draw_networkx_labels(graph.graph, pos=pos, font_weight='bold')
ax = plt.gca()
for e in graph.get_edges():
    ax.annotate("",
                xy=pos[e[0]], xycoords='data',
                xytext=pos[e[1]], textcoords='data',
                arrowprops=dict(arrowstyle="->", color="0.5",
                                shrinkA=5, shrinkB=5,
                                patchA=None, patchB=None,
                                connectionstyle="arc3,rad=rrr".replace('rrr',str(0.3*e[2])
                                ),
                                ),
                )
plt.axis('off')
plt.show()
```

```python
for (u, v, wt) in graph.get_edges().data('weight'):
    print(f"({u}, {v}, {wt:.6})")
```

    (eth, dai, 0.0029703)
    (eth, dai, 0.00182724)
    (eth, yam, 0.000330033)
    (eth, btc, 6.55738)
    (eth, btc, 16.6445)
    (dai, eth, 336.667)
    (dai, eth, 547.273)
    (dai, yam, 0.111111)
    (dai, yam, 0.0909091)
    (dai, btc, 9109.09)
    (yam, eth, 3030.0)
    (yam, dai, 9.0)
    (yam, dai, 11.0)
    (btc, eth, 0.1525)
    (btc, eth, 0.0600798)
    (btc, dai, 0.00010978)

```python
from Arbie.Variables.graph import FilteredTradingGraph

f_graph = FilteredTradingGraph(graph, 5)

for (u, v, wt) in f_graph.get_edges().data('weight'):
    print(f"({u}, {v}, {wt:.6})")
```

    (eth, dai, 0.00182724)
    (eth, yam, 0.000330033)
    (eth, btc, 6.55738)
    (dai, eth, 336.667)
    (dai, yam, 0.0909091)
    (yam, eth, 3030.0)
    (yam, dai, 9.0)
    (btc, eth, 0.1525)

```python
from Arbie.Actions.path_finder import PathFinder, create_trade
from Arbie.Actions import Store

st = Store()
st.add('unit_of_account', eth)
st.add('min_liquidity', 5)
pf = PathFinder(st)
cycles = pf.on_next(pools)
trades = create_trade(cycles[0])
trades
```

    [<Arbie.Actions.arbitrage.Trade at 0x7f8f9747c2b0>,
     <Arbie.Actions.arbitrage.Trade at 0x7f8f9747cfd0>,
     <Arbie.Actions.arbitrage.Trade at 0x7f8f9747ce20>]

```python
total_ratio = 1
value = 1
value_i = value
for trade in trades:
    price = trade.pool.spot_price(trade.token_in, trade.token_out)
    value_i = trade.pool.out_given_in(trade.token_in, trade.token_out, value_i)
    total_ratio *= price
    print(price)
print(f"Total ratio: {1/total_ratio}")
print(f"Total value after: {value}")
print(f"Ratio after: {value_i/value}")
```

    0.0018272425249169437
    0.0909090909090909
    3029.9999999999995
    Total ratio: 1.986798679867987
    Total value after: 1
    Ratio after: 1.75799109148745

```python
from sympy import *
from sympy.plotting import plot
init_printing()
```

```python
from Arbie.Actions.arbitrage import arbitrage_expr, arbitrage_diff_expr

x = symbols('x')
arb_expr = arbitrage_expr(trades)
arb_expr
```

```python
# If we plot we can clearly see that there seams to be some profit to be made!
plot(arb_expr, (x, 0, 15))
```

```python
from Arbie.Actions.arbitrage import find_arbitrage

balance = find_arbitrage(trades)
balance
```

    Balance(Token: eth, Value: 4.05461516215215)

```python
import pickle

with open("../data/arbie.bin", "r") as bin:
    store = pickle.load(bin)
```

```python

```


# **Gaussian Processes**

    Notebook version: 1.0 (Oct 06, 2015)

    Author: Jerónimo Arenas García (jarenas@tsc.uc3m.es)

    Changes: v.1.0 - First version
             v.1.1 - Figures changed to png (tiff not readable in Firefox)

```python
# Import some libraries that will be necessary for working with data and displaying plots

# To visualize plots in the notebook
%matplotlib inline

import matplotlib
import matplotlib.pyplot as plt
import numpy as np
import scipy.io       # To read matlab files
import pylab
```

# 4. Gaussian Processes

## 4.1 The multivariate Gaussian distribution

Let us assume that ${\bf x}$ and ${\bf y}$ are two jointly Gaussian random vectors, whose joint pdf can be expressed as

\begin{equation}
\left[ \begin{array}{c} {\bf x} \\ {\bf y} \end{array} \right] \sim {\cal{N}} \left( \left[ \begin{array}{c} {\bf m}_{\bf x} \\ {\bf m}_{\bf y} \end{array} \right] , \left[ \begin{array}{cc} {\pmb \Sigma}_{{\bf x}{\bf x}} & {\pmb \Sigma}_{{\bf x}{\bf y}} \\ {\pmb \Sigma}_{{\bf x}{\bf y}}^T & {\pmb \Sigma}_{{\bf y}{\bf y}}\end{array}\right] \right) = {\cal{N}} \left( \left[ \begin{array}{c} {\bf m}_{\bf x} \\ {\bf m}_{\bf y} \end{array} \right] , \left[ \begin{array}{cc} \widetilde{{\pmb \Sigma}}_{{\bf x}{\bf x}} & \widetilde{{\pmb \Sigma}}_{{\bf x}{\bf y}} \\ \widetilde{{\pmb \Sigma}}_{{\bf x}{\bf y}}^T & \widetilde{{\pmb \Sigma}}_{{\bf y}{\bf y}}\end{array}\right]^{-1} \right)
\end{equation}

where

   - ${\bf m}_{\bf x}$: Mean of random vector ${\bf x}$
   - ${\bf m}_{\bf y}$: Mean of random vector ${\bf y}$
   - ${\pmb \Sigma}_{{\bf x}{\bf x}}$: Covariance matrix of random vector ${\bf x}$
   - ${\pmb \Sigma}_{{\bf x}{\bf y}}$: Cross-covariance matrix of ${\bf x}$ and ${\bf y}$

Note that all blocks of the covariance matrix and its inverse are of appropriate sizes, as indicated by the subindexes. We are using tildes for the blocks of the inverse of the covariance matrix. Please, be aware that $\widetilde{\pmb\Sigma}_{{\bf x}{\bf x}} \neq {\pmb\Sigma}_{{\bf x}{\bf x}}^{-1}$.

Since random vectors are jointly Gaussian, this means that marginal and conditional distributions are also Gaussian, with distributions that can be expresses as

$${\bf x} \sim {\cal{N}} ({\bf m}_{\bf x}, {\pmb \Sigma}_{{\bf x}{\bf x}}) \qquad \qquad {\bf y} \sim {\cal{N}} ({\bf m}_{\bf y}, {\pmb \Sigma}_{{\bf y}{\bf y}})$$

$${\bf x}|{\bf y} \sim {\cal{N}} ( {\bf m}_{\bf x} - \widetilde{{\pmb \Sigma}}_{{\bf x}{\bf x}}^{-1} \widetilde{{\pmb \Sigma}}_{{\bf x}{\bf y}} ({\bf y} - {\bf m}_{\bf y}), \widetilde{{\pmb \Sigma}}_{{\bf x}{\bf x}}^{-1})$$

$${\bf x}|{\bf y} \sim {\cal{N}} ( {\bf m}_{\bf x} + {{\pmb \Sigma}}_{{\bf x}{\bf y}} {{\pmb \Sigma}}_{{\bf y}{\bf y}}^{-1} ({\bf y} - {\bf m}_{\bf y}), {{\pmb \Sigma}}_{{\bf x}{\bf x}} - {\pmb \Sigma}_{{\bf x}{\bf y}} {{\pmb \Sigma}}_{{\bf y}{\bf y}}^{-1} {\pmb \Sigma}_{{\bf x}{\bf y}}^\top)$$

The demonstration of these expressions is left as an exercise

### Generation of Samples from a Multivariate Gaussian Distribution

Although there are python routines that can help to generate points from a multivariate Gaussian distribution, a standard procedure is based upon the generation of samples from the normal distribution with zero mean and unitary standard deviation, which are then transformed in an appropritate manner.

The Cholesky decomposition of a symmetric positive-definite matrix ${\bf A}$ is (the definition can be slightly modified for complex matrices, but here we restrict our selves to matrices with real values):

$${\bf A} = {\bf L} {\bf L}^\top$$

where ${\bf L}$ is a lower triangular matrix with positive diagonal entries. It can be shown that such decomposition exists and is unique, and can be computed in a very stable an efficient way.

Now, imaging the problem of drawing points from the Gaussian distribution

$$p({\bf x}) = {\cal N}\left({\bf m},{\pmb \Sigma} \right)$$

This distribution can be sampled by drawing points ${\bf x}'$ from ${\cal N}\left({\bf 0},{\bf I} \right)$, and then applying the transformation

$${\bf x} = {\bf L} {\bf x}'~+~{\bf m}$$

where ${\bf L}$ is obtained from the Cholesky decomposition of $\pmb \Sigma$.

The demonstration of the validity of the process goes along these lines:

   - Since ${\bf x}$ is a linear transformation of a Gaussian variable, it should also be normally distributed

   - ${\mathbb E}\{{\bf x}\} = {\bf L} \mathbb{E}\{{\bf x}'\} + {\bf m} = {\bf m}$

   - $\text{Cov}\{{\bf x}\} = \text{Cov}\{{\bf L} {\bf x}'\} = {\mathbb E} \{{\bf L} {\bf x}' {{\bf x}'}^\top {\bf L}^\top\} = {\bf L} {\bf L}^\top = {\pmb \Sigma}$

```python
#This code illustrates the generation of multivariate Gaussian points with arbitrary distribution
m = np.array([3,3])
V = np.array([[1,-0.95],[-0.95,1]])

L = np.linalg.cholesky(V)

#Generation of points from the normal distribution with zero mean and unit variance
n_points = 300
dim = 2
x_prime = np.random.randn(dim,n_points)

#Linear transformation
x = L.dot(x_prime) + np.matrix(m).T

#Plot the results
plt.plot(x_prime[0,],x_prime[1,],'b.');
plt.plot(x[0,],x[1,],'r.');
```

## 4.2 Non-parametric Inference with Gaussian Processes

In the previous lesson we assumed the following generative model for the patterns in the training set

$${\bf s} = {\bf f} + {\pmb \varepsilon}$$

where

   - ${\bf s}$ is a vector containing all target values in the training set

   - ${\bf f}$ is a vector containing the true values of the function we would like to approximate at all points ${\bf x}^{(k)}$ in the training set

   - ${\pmb \varepsilon}$ is a vector of i.i.d. noise samples, whose distribution is assumed ${\cal N}({\bf 0},\sigma_\varepsilon^2{\bf I})$

This model essentially tells us that the observations in the training set are the values of a hidden function $f({\bf x})$ contaminated by additive noise.

   - In the previous lesson, we assumed a parametric model ${\bf f} = {\bf Z}{\bf w}$, and use it to make Bayesian Inference on the weight vector characterizing the model

   - In this lesson we will proceed non-parametrically:

      * We do not assume any particular analytic shape for $f({\bf x})$

      * Inference is done directly on the values of the function, i.e., on ${\bf f}$

How can we made inference directly on the function values?  The answer is provided by the concept of Gaussian Process (GP)

### 4.2.1 Gaussian Processes

   - A Gaussian process is a collection of random variables, any finite number of which have (consistent) Gaussian distributions

   - You can think of a Gaussian Process as a collection of infinite random variables with joint Gaussian distribution

   - The standard way we will use them is to characterize the joint distribution of the values of $f({\bf x})$

   - In this way, we can generate the values of $f({\bf x})$ according to the joint distribution of the points, if the values of $({\bf x})$ are close enough, this will produce a regression curve

In other words, rather than having a parametric model, and sampling ${\bf w}$ from a given distribution, GPs allow us to directly sample the values of the function

#### Characterization of a Gaussian Process

   - A Gaussian distribution is characterized by a mean vector and and a covariance matrix

    $${\bf f}~\sim~{\cal N}\left({\bf m},{\pmb \Sigma}\right)$$

   - A Gaussian Process is characterized by a mean function and a covariance function

    $$f({\bf x})~\sim~{\cal GP} \left( m_{\bf x}, k({\bf x},{\bf x'}) \right)$$

   - A typical selection for the covariance function is the Squared Exponential (SE) kernel

    $$k({\bf x},{\bf x'}) = \exp\left( -\frac{1}{2l} (\|{\bf x}-{\bf x'}\|^2)\right)$$

Intuitively, this kernel specifies that the values of $f({\bf x})$ and $f({\bf x}')$ should be more correlated the closer the two points ${\bf x}$ and ${\bf x}'$.  What we consider to be close or far, is modulated by the selection of the length-parameter $l$.

#### Example: Using a Gaussian Process to generate functions

The concept of Gaussian Process is probably better illustrated with the following code fragment, where we generate functions from a GP with mean $m({\bf x}) = 0$ and SE covariance function

```python
from scipy import spatial

pylab.subplots_adjust(hspace=0.7)
n_points = 300
n_curves = 5
l_values = [.001, .01, 0.1]

X_grid = np.linspace(-2,2,n_points)
X_grid = np.matrix(X_grid).T

color = 'rgbcm'

for i,l in enumerate(l_values):
    plt.subplot(len(l_values),1,i+1)

    # The GP allow us to obtain the Gaussian distribution of the
    # values of f for any particular selection of points x
    # We do so for the points in X_grid

    # We compute the distances among each pair of points in X_grid
    distances = spatial.distance.cdist(X_grid,X_grid,'euclidean')
    # And the covariance matrix
    K = np.exp(-np.power(distances,2)/(2*l))
    # For numerical reasons: we add a small constant along the main
    # diagonal to make sure K is positive-definite
    L = np.linalg.cholesky(K+1e-10*np.eye(n_points))

    # Next, we draw n_curves samples from the Gaussian distribution
    # with mean 0 and covariance matrix K
    for iter in range(n_curves):
        f_prime = np.random.randn(n_points,1)
        f = L.dot(f_prime)
        plt.plot(X_grid,f,color[iter%n_curves])

    plt.title('Length-scale parameter: '+str(l))
```

### 4.2.2 Modeling the posterior distribution of the targets

Let us assume that we wish to make predictions of the target variable at a collection of points $\{{{\bf x}^*}_0, {{\bf x}^*}_1, \dots, {{\bf x}^*}_{K-1}\}$. Let us put together the corresponding values of $f(\cdot)$ into a column vector ${\bf f}^*$. For the particular case in which we need to make predictions at a single point, such vector would become a scalar

#### Joint distribution of ${\bf f}$ and ${\bf f}^*$

Using the generative model:

$$
s_k = f({\bf x}_k) + \varepsilon_k$$

and assuming further that $f(\bf x)$ is a GP with mean 0 and covariance function $k({\bf x},{\bf x}')$, we can model the joint distribution of ${\bf f}$ and ${\bf f}^*$ as

$$\left[\begin{array}{c}{\bf f}\\{\bf f}^*\end{array}\right]~\sim~{\cal N}\left({\bf 0},\left[\begin{array}{cc}{\bf K} & {\bf K}_*^\top \\ {\bf K}_* & {\bf K}_{**} \end{array}\right]\right)$$

where:

   - ${\bf K}$ contains the covariance among the components of ${\bf f}$
   - ${\bf K}_{**}$ contains the covariance among the components of ${\bf f}^*$
   - ${\bf K}_{*}$ contains the covariance between components of ${\bf f}^*$ and ${\bf f}$

#### Joint distribution of ${\bf s}$ and ${\bf f}^*$

Since ${\bf s} = {\bf f} + {\pmb \varepsilon}$, and since the noise is independent of the values of ${\bf f}$, we have that

$$\text{Cov}\{{\bf s}\} = \text{Cov}\{{\bf f}\} + \text{Cov}\{{\pmb \varepsilon}\} = {\bf K} + \sigma_\varepsilon^2 {\bf I}$$

Next, we can also see that

$$\text{Cov}\{{\bf f}^*,{\bf s}\} = \text{Cov}\{{\bf f}^*,({\bf f}+{\pmb \varepsilon})\} = \text{Cov}\{{\bf f}^*,{\bf f}\} = {\bf K}_*$$

Therefore, the joint distribution of ${\bf s}$ and ${\bf f}^*$ is

$$\left[\begin{array}{c}{\bf s}\\{\bf f}^*\end{array}\right]~\sim~{\cal N}\left({\bf 0},\left[\begin{array}{cc}{\bf K} + \sigma_\varepsilon^2 {\bf I}& {\bf K}_*^\top \\ {\bf K}_* & {\bf K}_{**} \end{array}\right]\right)$$

#### Predictive distribution of ${\bf f}^*$ given ${\bf s}$

Using the results of Section 4.1.1, it is now straightforward to obtain the distribution of ${\bf f}^*$ given ${\bf s}$:

$${\bf f}^* | {\bf s}~\sim~{\cal N}\left({\bf K}_*[{\bf K+\sigma_\varepsilon^2 {\bf I}}]^{-1}{\bf s}, {\bf K}_{**} - {\bf K}_* [{\bf K+\sigma_\varepsilon^2 {\bf I}}]^{-1} {\bf K}_*^\top\right)$$

### 4.2.3 Gaussian Processes in practice

#### Predictive distribution computation

```python
n_points = 15
n_grid = 200
frec = 3
std_n = 0.2

#Generating the training points
X_tr = 3 * np.random.random((n_points,1)) - 0.5
S_tr = - np.cos(frec*X_tr) + std_n * np.random.randn(n_points,1)
#These will be the points where the model will be evaluated
X_grid = np.linspace(-1,3,n_grid)

fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(X_tr,S_tr,'b.',markersize=10)
ax.set_xlim(-.5,2.5)

#GP hyperparameters
sigma_eps = 0.1
l = 0.05

#Covariance matrix computation
X_tr = np.matrix(X_tr)
X_grid = np.matrix(X_grid).T

dist = spatial.distance.cdist(X_tr,X_tr,'euclidean')
dist_ss = spatial.distance.cdist(X_grid,X_grid,'euclidean')
dist_s = spatial.distance.cdist(X_grid,X_tr,'euclidean')

K = np.exp(-np.power(dist,2)/(2*l))
K_ss = np.exp(-np.power(dist_ss,2)/(2*l))
K_s = np.exp(-np.power(dist_s,2)/(2*l))

#Posterior distribution of f_star
m = K_s.dot(np.linalg.inv(K + sigma_eps**2 * np.eye(n_points))).dot(S_tr)
m = np.array(m).flatten()
Cov = K_ss - K_s.dot(np.linalg.inv(K + sigma_eps**2 * np.eye(n_points))).dot(K_s.T)
#Variance of each prediction
v = np.diagonal(Cov)

plt.plot(X_grid,m,'r',label='Predictive mean')
plt.fill_between(np.array(X_grid).flatten(), m-np.sqrt(v), m+np.sqrt(v),
         alpha=0.2, edgecolor='#1B2ACC', facecolor='#089FFF',
         linewidth=4, linestyle='dashdot', antialiased=True)
plt.show()
```

You can play modify the values of $\mbox{sigma_eps}$ and $\mbox{l}$, to see how the predictive distribution changes

## 4.3 Maximum likelihood selection of hyperparameters

As we did in the previous unit, we can find select the value of the hyperparameters to maximize the likelihood of the model. To this end, let us note that the pdf of ${\bf s}$ for given hyperparameters $\sigma_\varepsilon$ and $l$ is given by

$$p({\bf s}|\sigma_\varepsilon,l) = {\cal N}({\bf 0},{\bf K} + \sigma_\varepsilon^2 {\bf I}) = \frac{1}{(2 \pi)^{K/2}\mid{\bf K} + \sigma_\varepsilon^2 {\bf I}\mid^{1/2}} \exp\left(-\frac{1}{2}{\bf s}^\top ({\bf K} + \sigma_\varepsilon^2 {\bf I})^{-1} {\bf s}\right)$$

Instead of directly maximizing this expression, it is convenient to take the log, arriving at

$$\log p({\bf s}|\sigma_\varepsilon,l) = - \frac{K}{2}\log(2\pi) - \frac{1}{2} \log \mid{\bf K} + \sigma_\varepsilon^2 {\bf I}\mid -\frac{1}{2}{\bf s}^\top ({\bf K} + \sigma_\varepsilon^2 {\bf I})^{-1} {\bf s}$$

(be aware of the difference between $K$, the number of points in the training dataset, and ${\bf K}$, the covariance matrix of ${\bf f}$)

Selection of hyperparameters can be done with the objective of maximize this marginal likelihood, as illustrated next

```python
from math import pi

n_points = 15
frec = 3
std_n = 0.2

#Generating the training points
X_tr = 3 * np.random.random((n_points,1)) - 0.5
S_tr = - np.cos(frec*X_tr) + std_n * np.random.randn(n_points,1)

#GP hyperparameters
sigma_eps = 0.3
l = np.logspace(-5,3,100)

#Covariance matrix computation
X_tr = np.matrix(X_tr)
X_grid = np.matrix(X_grid).T

dist = spatial.distance.cdist(X_tr,X_tr,'euclidean')

log_E = []

for l_iter in l:
    K = np.exp(-np.power(dist,2)/(2*l_iter))
    log_E_iter = -(n_points/2)*np.log(2*pi) \
        - np.log(np.linalg.det(K + sigma_eps**2 * np.eye(n_points))) \
        - 0.5 * S_tr.T.dot(np.linalg.inv(K + sigma_eps**2 * np.eye(n_points))).dot(S_tr)
    log_E.append(log_E_iter[0,0])

plt.semilogx(l,log_E),
plt.xlabel('length-scale hyperparameter')
plt.ylabel('Log likelihood')

selected_l = l[np.argmax(log_E)]

```

```python
n_grid = 200

#These will be the points where the model will be evaluated
X_grid = np.linspace(-1,3,n_grid)

fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(X_tr,S_tr,'b.',markersize=10)
ax.set_xlim(-.5,2.5)

#GP hyperparameters
sigma_eps = 0.3
l = selected_l

#Covariance matrix computation
X_tr = np.matrix(X_tr)
X_grid = np.matrix(X_grid).T

dist = spatial.distance.cdist(X_tr,X_tr,'euclidean')
dist_ss = spatial.distance.cdist(X_grid,X_grid,'euclidean')
dist_s = spatial.distance.cdist(X_grid,X_tr,'euclidean')

K = np.exp(-np.power(dist,2)/(2*l))
K_ss = np.exp(-np.power(dist_ss,2)/(2*l))
K_s = np.exp(-np.power(dist_s,2)/(2*l))

#Posterior distribution of f_star
m = K_s.dot(np.linalg.inv(K + sigma_eps**2 * np.eye(n_points))).dot(S_tr)
m = np.array(m).flatten()
Cov = K_ss - K_s.dot(np.linalg.inv(K + sigma_eps**2 * np.eye(n_points))).dot(K_s.T)
#Variance of each prediction
v = np.diagonal(Cov)

plt.plot(X_grid,m,'r',label='Predictive mean')
plt.fill_between(np.array(X_grid).flatten(), m-np.sqrt(v), m+np.sqrt(v),
         alpha=0.2, edgecolor='#1B2ACC', facecolor='#089FFF',
         linewidth=4, linestyle='dashdot', antialiased=True)
plt.show()
```

```python

```

```python

```


# Probabilidad I:
Valor esperado e indicadores. Teorema de Bayes. Estimación Bayesiana.

# 0. PMFs y PDFs conjuntas.

- Una PMF conjunta $p_{X,Y}$ de las variables $X$ y $Y$ está definida como

\begin{equation}
p_{X,Y}(x,y)=P(X=x,Y=y)
\end{equation}

- La PMF marginal de X y Y puede ser obtenida a partir de la PMF conjunta, utilizando

\begin{equation}
p_{X}(x)=\sum_{y}{p_{X,Y}(x,y)}
\end{equation}
y
\begin{equation}
p_{Y}(y)=\sum_{x}{p_{X,Y}(x,y)}
\end{equation}

De manera análoga, para $X$ y $Y$ que sean variables aleatorias conjuntas con una PDF conjunta $f_{X,Y}$ se tiene que

\begin{equation}
f_{X,Y}(x,y)=f_{Y}(y)f_{X|Y}(x|y)
\end{equation}

\begin{equation}
f_{X}(x)=\int_{-\infty}^{\infty}f_{Y}(y)f_{X|Y}(x|y)dy
\end{equation}

#### Ejercicio 1: Realice dos gráficas de funciones de distribución conjuntas para variables aleatorias X,Y distribuidas normales para medias y varianzas iguales y medias y varianzas diferentes

Fórmula para distribución normal multivariada

\begin{equation}
f_X (x_1, ...,x_n)=\frac{1}{(2\pi)^{n/2}|\Sigma|^{1/2}} \exp{-\frac{1}{2}(x-\mu)^T \Sigma (x-\mu)}
\end{equation}

```python
import numpy as np
import matplotlib.pyplot as plt

#Utilizando una función gaussiana como la utilizada previamente
def gauss(x,m,s):
    return 1/(np.sqrt(2*np.pi)*s)*np.exp(-(x-m)**2 / (2*s**2))

x_0=np.linspace(-3,3,600)
x=gauss(x_0,0,1)
y=gauss(x_0,0,1)
xx,yy=np.meshgrid(x,y)
img=xx*yy
plt.contourf(x_0,x_0,img)
plt.xlabel('x')
plt.ylabel('y')
plt.show()

```

```python

#Sin embargo, al hacer esto, no obtenemos la distribución normal multivariada dadas las condicionales de x, y.
#Utilizando la función multivariate_normal de scipy, podemos obtenerla, esta función recibe como parámetro
#la media [x,y] y la matriz de covarianza [[c1,c2],[c3,c4]]. Luego podemos graficar la pdf utilizando rv.pdf

from scipy.stats import multivariate_normal

x, y = np.mgrid[-3:3:1.0/100, -3:3:1.0/100]
pos = np.empty(x.shape + (2,))
pos[:, :, 0] = x; pos[:, :, 1] = y
rv = multivariate_normal([0, 0], [[1, 0], [0, 1]])
plt.contourf(x, y, rv.pdf(pos))
plt.title('Distribución gaussiana 2D')
plt.xlabel('x')
plt.ylabel('y')
plt.show()
```

# 1. Valor Esperado y varianza.

Para variables discretas se define el valor esperado de una variable discreta $X$ como

\begin{equation}
E[X] =\sum_{x}{x p_{X} (x)}
\end{equation}

y para una variable continua:

\begin{equation}
E[X] =\int_{x}{x p_{X} (x)}dx
\end{equation}

y la varianza se define como

\begin{equation}
var[X] = E[(X-E[X])^2]
\end{equation}

#### Ejercicio 2: Obtenga numéricamente el valor esperado y la varianza de una función de distribución Gamma generada aleatoriamente con parámetros $1/\lambda=0.5$ y $k=9$. Realice la gráfica de la distribución y de la CDF.

```python
#Realizaremos la gráfica de una distribución gamma con los parámetros dados
shape,scale=9,0.5
g=np.random.gamma(shape,scale,1000)
(n, bins, patches)=plt.hist(g,bins=100,density=True)
plt.title(r'histograma de distribución $\Gamma(0.5,9)$')
plt.show()

#Utilizaremos los bins y n para graficar un estimado de la CDF de esta distribución

plt.plot(bins[:-1],np.cumsum(n)/sum(n))
plt.title('CDF estimada a partir de bins')
plt.show()
```

# 2. Teorema de Bayes

Volviendo a los fundamentos de probabilidad, hablemos del teorema de probabilidad total, que dicta: Sean $A_1, ..., A_n$ eventos disjuntos que forman una partición del espacio muestral (cada posible resultado está incluido en exactamente uno de los eventos $A_1, ...., A_n$) y asumamos que $P(A_i)>0$ para todo $i$. Entonces, para cada evento $B$, se tiene

\begin{equation}
P(B)=P(A_1 \cap B)+P(A_n \cap B)
\end{equation}

\begin{equation}
=P(A_1)P(B|A_1)+...+P(A_n)P(B|A_n).
\end{equation}

este teorema lo que dice es que, intuitivamente, estamos particionando el espacio muestral en un número de escenarios $A_i$. Entonces, la probabilidad que $B$ ocurra es un promedio ponderado de la probabilidad condicional sobre cada escenario, donde cada escenario es pesado de acuerdo con su probabilidad. Esto permite calcular la probabilidad de varios eventos $B$ para los cuales las probabilidades $P(B|A_i)$ son conocidas o fáciles de obtener. El teorema de probabilidad total puede ser aplicado repetidamente para calcular probabilidades en experimentos que tienen caracter secuencial.

## Inferencia y regla de Bayes.

El teorema de probabilidad total es usualmente usado en conjunto con el teorema de Bayes, que relaciona las probabilidades condicionales de la forma $P(A|B)$ cno probabilidades condicionales de la forma $P(B|A)$, en el cual el orden del condicionamiento es contrario.

#### Regla de Bayes
Sean $A_1, A_2, ..., A_n$ eventos disjuntos que forman una partición del espacio muestral, y asumiendo que $P(A_i)>0 $ para todo $i$. Entonces para algún evento tal que $P(B)>0$, se tiene
\begin{equation}
P(A_i|B)=\frac{P(A_i)P(B|A_i)}{P(B)}
\end{equation}

\begin{equation}
=\frac{P(A_i)P(B|A_i)}{P(A_1)P(B|A_1)+...+P(A_n)P(B|A_n)}
\end{equation}

La regla de Bayes es usualmente usada para inferencia. Existen un cierto número de "causas" que producen un "efecto". Es posible observar el efecto y se desea inferir la causa. Los eventos $A_1,...,A_n$ son asociados con las causas y el evento $B$ representa el efecto. La probabilidad $P(B|A_i)$ de que el efecto observado cuando la causa $A_i$ está presente, contribuye a un modelo probabilistico de relación de causa y efecto. Dado el efecto B que se ha observado, se desea evaluar la probabilidad $P(A_i|B)$ de que la causa $A_i$ esté presente. En general se refiere a $P(A_i|B)$ como la probabilidad a posteriori de un evento $A_i$ y a $P(A_i)$ la cual se denomina probabilidad a priori.

##### Nota:

Es importante recordar el concepto de independencia, que dice que cuando $P(A|B)=P(A)$, entonces A es independiente de B. Por lo que por definición:

\begin{equation}
P(A\cup B)=P(A)P(B)
\end{equation}

En el caso en que $P(B)=0$, entonces $P(A|B)$ no está definida, por lo que la relación se mantiene. La simetría de esta relación también implica que la independencia es una propiedad simétrica; es decir, que si A es independiente de B, entonces B es independiente de A, y se puede decir que A y B son eventos independientes.

Además, también es importante notar que la independencia condicional entre un evento C y dos eventos B y C, es condicional si

\begin{equation}
P(A\cup B| C)=P(A|C)P(B|C)
\end{equation}

Que significa, en otras palabras, que si se sabe que C ocurrió, el conocimiento adicional de que B también ocurrió no cambia la probabilidad de A. La independencia entre dos eventos A y B respecto a una ley de probabilidad incondicional no implica la independencia condicional y viceversa.

# 3 Inferencia Bayesiana

La inferencia estadística es el proceso de extraer información a partir de una variable o un modelo desconocido a partir de información disponible.

La infrencia estadística difiere de la teoría de probabilidad en formas fundamentales. La probabilidad es un área de las matemáticas completamente autocontenida, basada en axiomas, como ya se ha visto. En razonamiento probabilístico se asume un modelo probabilístico completamente especificado que obedece estos axiomas. Luego se hace uso de un método matemático para cuantificar las consecuencias de este modelo o responder varias preguntas de interés. En particular, cada pregunta no ambigua tiene una respuesta correcta única.

La estadística es diferente. Para cualquier problema, pueden existir múltiples métodos razonables, con diferentes respuestas. En general, no hay una forma de obtener el mejor método, a menos que se realicen suposiciones fuertes y se impongan restricciones adicionales sobre la inferencia.

En la estadística Bayesiana, todas las suposiciones se localizan en un lugar, en la forma de un prior, los estadístas Bayesianos argumentan que todas las suposiciones son traidas a una superficie y están dispuestas al escrutinio.

Finalmente hay consideraciones prácticas. En muchos casos, los métodos Bayesianos son computacionalmente intratables. Sin embargo, con las capacidades de cómputo recientes, gran parte de la comunidad se enfoca en realizar métodos Bayesianos más prácticos y aplicables.

Conceptos clave:

- En estadística Bayesiana, se tratan los parámetros desconocidos como variables aleatorias con distribuciones a priori conocidas.

- En estimación de parámetros se quiere generar un estimado de qué tan cercanos están los valores de los estimadores a los verdaderos valores de los parámetros en un sentido probabilístico.

- En pruebas de hipótesis, el parámetro desconocido toma uno de valores infinitos, correspondiente según la hipótesis. Se quiere seleccionar una hipótesis basados en una pequeña probabilidad de error.

- Los principales métodos de inferencia Bayesiana son:

	- MAP: (Maximun a posteriori probability): A partir de posibles valores de parámetros, se selecciona uno con máxima probabilidad condicional dados unos datos.

	- LMS: (Least Mean Squares) Se selecciona un estimador/función de los datos que minimiza el error cuadrático medio entre el parámetro y su estimado.

	- Linear Least Mean Squares: Se selecciona un estimador que es una función lineal de los datos y minimiza el error cuadrático medio entre los parámetros y su estimado.

En inferencia bayesiana, la cantidad de interés se denota por $\Theta$, y es modelada como una variable aleatoria o como una colección finita de variables aleatorias. Aquí, $\theta$ puede representar cantidades físicas, tales como una velocidad o una posición, o un conjunto de parámetros desconocidos de un modelo probabilístico. Por simpleza, a menos que lo contrario sea explícitamente mencionado, se ve $\Theta$ como una variable aleatoria.

El objetivo es extraer información acerca de $\theta$, basado en observar una colección de variables aleatorias $X=(X_1,...,X_n)$ relacionadas, llamadas observaciones, medidas o vector de observaciones. Por esto, se asume que se conoce la distribución conjunta de $\Theta$ y $X$. Es decir,

- Se asume el conocimiento de la distribución a priori $p_\theta$, dependiendo de si $\theta$ es discreta o continua-

- Una distribución condicional $p_{X|\theta}$, dependiendo de si X es discreta o continua.

Una vez un valor particular de $x$ en $X$ ha sido observado, una respuesta completa de un problema de inferencia es proveído por la distribución a posteriori. Esta distribución está determinada por la forma apropiada de la regla de Bayes y encapsula todo el conocimiento que se pueda tener acerca de $\Theta$ dada la información disponible.

### Resumen:

- Se comienza con una distribución a priori $p_{\Theta}$ o $f_{\Theta}$ para una variable desconocida aleatoria $\Theta$.

- Se tiene un modelo $p_{X|\Theta}$ o $f_{X|\Theta}$ del vector se observaciones X.

- Después de observar el valor $x$ en $X$, se forma la distribución a posteriori de $\Theta$, usando la versión apropiada de la regla de Bayes.

##### Ejercicio:
Suponga que tratamos con las dos siguientes hipótesis

\begin{equation}
H_1:p=0.1,H_2:p=0.2
\end{equation}

donde $p=0.2$ está basada en una proporción muestral de 1 a 5.

Primero asumamos que el tamaño de la muestra es $n=5$, $k=1$ donde, k significa una pelota amarilla que escojemos entre n pelotas. También podemos asumor que la probailidad a priori es 0.5 y 0.5, es decir igual para ambas hipótesis. Es posible actualizar la probabilidad a posteriori de cada hipótesis utilizando la regla de Bayes. Calcule las probabilidades P(p=0.1) y P(p=0.2) respectivamente para muestras de tamaño 5, 10, 15 y 20 y concluya al respecto.

```python
from scipy.stats import binom

n=[5,10,15,20]

p1=0.1
p2=0.2

k=1

for n_ in n:
    p_1= binom(n_, p1)
    p_2=binom(n_,p2)
    """
    P1=p_1.pmf(k)*0.5/(p_1.pmf(k)*0.5+p_2.pmf(k)*0.5)
    P2=p_2.pmf(k)*0.5/(p_1.pmf(k)*0.5+p_2.pmf(k)*0.5)

    print(".............")

    print("P(H1):%lf para n=%d"%(P1,n_))
    print("P(H2):%lf para n=%d"%(P2,n_))
    """
    P1=p_1.pmf(k)*0.5
    P2=p_2.pmf(k)*0.5

    nm=P1+P2

    print(".............")

    print("P(H1):%lf para n=%d"%(P1/nm,n_))
    print("P(H2):%lf para n=%d"%(P2/nm,n_))

```

    .............
    P(H1):0.444723 para n=5
    P(H2):0.555277 para n=5
    .............
    P(H1):0.590710 para n=10
    P(H2):0.409290 para n=10
    .............
    P(H1):0.722283 para n=15
    P(H2):0.277717 para n=15
    .............
    P(H1):0.824151 para n=20
    P(H2):0.175849 para n=20

# Referencias.

- Wackerly, D. Mathematical Statistics with Applications. 2008
- Bertsekas, D. Introduction to Probability. 2008.
- Sivia, D.S. Data Analysis: A Bayesian Tutorial. 2006.

```python

```


# Monte Carlo Markov Chain

## Christina Lee

## Category: Numerics

### Monte Carlo Physics Series
* [Monte Carlo: Calculation of Pi](../Numerics_Prog/Monte-Carlo-Pi.ipynb)
* [Monte Carlo Markov Chain](../Numerics_Prog/Monte-Carlo-Markov-Chain.ipynb)
* [Monte Carlo Ferromagnet](../Prerequisites/Monte-Carlo-Ferromagnet.ipynb)
* [Phase Transitions](../Prerequisites/Phase-Transitions.ipynb)

### Intro

If you didn't check it out already, take a look at the post that introduces using random numbers in calculations.  Any such simulation is a Monte Carlo simulation.  The most used kind of Monte Carlo simulation is a Markov Chain, also known as a random walk, or drunkard's walk.  A Markov Chain is a series of steps where
* each new state is chosen probabilistically
* the probabilities only depend on the current state (no memory)

Imagine a drunkard trying to walk.  At any one point, they could progress either left or right rather randomly.  Also, just because they had been traveling in a straight line so far does not guarantee they will continue to do.  They've just had extremely good luck.

We use Markov Chains to approximate probability distributions.

To create a good Markov Chain, we need
*  Ergodicity: All states can be reached
*  Global Balance: A condition that ensures the proper equilibrium distribution

### The Balances

Let $\pi_i$ be the probability that a particle is at site $i$, and $p_{ij}$ be the probability that a particle moves from $i$ to $j$.  Then Global Balance can be written as,

\begin{equation}
\sum\limits_j \pi_i p_{i j} = \sum\limits_j \pi_j p_{j i} \;\;\;\;\; \forall i.
\end{equation}

In non-equation terms, this says the amount of "chain" leaving site $i$ is the same as the amount of "chain" entering site $i$ for every site in equilibrium.  There is no flow.

Usually though, we actually want to work with a stricter rule than Global Balance,  Detailed Balance , written as

\begin{equation}
\pi_i p_{i j} = \pi_j p_{j i}.
\end{equation}

Detailed Balance further constricts the transition probabilities we can assign and makes it easier to design an algorithm. Almost all MCMC algorithms out there use detailed balance, and only lately have certain applied mathematicians begun looking at breaking detailed balance to increase efficiency in certain classes of problems.

### Today's Test Problem

I will let you know now; this might be one of the most powerful numerical methods you could ever learn.  I was going to put down a list of applications, but the only limit to such a list is your imagination.

Today though, we will not be trying to predict stock market crashes, calculate the PageRank of a webpage, or calculate the entropy a quantum spin liquid at zero temperature.  We just want to calculate an uniform probability distribution, and look at how Monte Carlo Markov Chains behave.

* We will start with an $l\times l$ grid

* Our chain starts somewhere in that grid

* We can then move up, down, left or right equally

* If we hit an edge, we come out the opposite side (toroidal boundary conditions)

First question! Is this ergodic?

Yes! Nothing stops us from reaching any location.

Second question! Does this obey detailed balance?

Yes!  In equilibrium, each block has a probability of $\pi_i = \frac{1}{l^2}$, and can travel to any of its 4 neighbors with probability of $p_{ij} = \frac{1}{4}$.  For any two neighbors
\begin{equation}
\frac{1}{l^2}\frac{1}{4} = \frac{1}{l^2}\frac{1}{4},
\end{equation}
and if they are not neighbors,
\begin{equation}
0 = 0.
\end{equation}

```julia
using Statistics
using Plots
gr()
```

    Plots.GRBackend()

```julia
# This is just the equivalent of `mod`
# for using in an array that indexes from 1.
function armod(i,j)
    return (mod(i-1+j,j)+1)
end
```

    armod (generic function with 1 method)

```julia
# input the size of the grid
l=5;
n=l^2;
```

```julia
function Transition(i)
    #randomly chose up, down, left or right
    d=rand(1:4);
    if d==1 #if down
        return armod(i-l,n);

    elseif d==2 #if left
        row=convert(Int,floor((i-1)/l));
        return armod(i-1,l)+l*row;

    elseif d==3 #if right
        row=convert(Int,floor((i-1)/l));
        return armod(i+1,l)+l*row;

    else  # otherwise up
        return armod(i+l,n);
    end
end
```

    Transition (generic function with 1 method)

```julia
# The centers of blocks.
# Will be using for pictoral purposes

pos=zeros(Float64,2,n);
pos[1,:]=[floor((i-1)/l) for i in 1:n].+0.5;
pos[2,:]=[mod(i-1,l) for i in 1:n].+0.5;
```

```julia
# How many timesteps
tn=2000;

# Array of timesteps
ti=Array{Int64,1}()
# Array of errors
err=Array{Float64,1}()

# Stores current location, initialized randomly
current=rand(1:n);
# Stores last location, used for pictoral purposes
last=current;

#Keeps track of where chain went
Naccumulated=zeros(Int64,l,l);

# put in our first point
# can index 2d array as 1d
Naccumulated[current]+=1;
```

```julia
for ii in 1:tn

    last=current;
    # Determine the new point
    current=Transition(current);
    Naccumulated[current]+=1;

    # add new time steps and error points
    push!(ti,ii)
    push!(err,std(Naccumulated/ii))

end
```

When I was using an old version and pyplot I created this video of the state at each time point. https://www.youtube.com/watch?v=gxX3Fu1uuCs

```julia
heatmap(Naccumulated/tn .- 1/l^2)
```

```julia
scatter(ti,log10.(err), markerstrokewidth=0)

plot!(xlabel="Step"
    ,ylabel="Log10 std"
    ,title="Error for a $l x$l")
```

So, after running the above code and trying to figure out how it works (mostly plotting stuff), go back and study some properties of the system.

* How long does it take to forget it's initial position?

* How does the behaviour change with system size?

* How long would you have to go to get a certain accuracy? (especially if you didn't know what distribution you where looking for)

So hopefully you enjoyed this tiny introduction to an incredibly rich subject.  Feel free to explore all the nooks and crannies to really understand the basics of this kind of simulation, so you can gain more control over the more complex simulations.

Monte Carlo simulations are as much of an art as a science.  You need to live them, love them, and breathe them till you find out exactly why they are behaving like little kittens that can finally jump on top of your countertops, or open your bedroom door at 1am.

For all their misbehaving, you love the kittens anyway.

@ARTICLE{1970Bimka..57...97H,
    title = "{Monte Carlo Sampling Methods using Markov Chains and their Applications}",
  journal = {Biometrika, Vol.~57, No.~1, p.~97-109, 1970},
     year = 1970,
    month = apr,
   volume = 57,
    pages = {97-109},
      doi = {10.1093/biomet/57.1.97},
   adsurl = {http://adsabs.harvard.edu/abs/1970Bimka..57...97H},
  adsnote = {Provided by the SAO/NASA Astrophysics Data System}
}

```julia

```