# Lecture 20

## Expected distance between Normals, Multinomial, Cauchy

In lecture 19, we saw how to obtain the expected distance between two uniform r.v. How would we do the same, but with the _Normal distribution_?

### Example: expected distance between 2 Normally distributed random points $\mathbb{E} | Z_1 - Z_2 |$

Given $Z_1,Z_2 \sim \mathcal{N}(0,1)$, $Z_1$ and $Z_2$ are i.i.d., can we find $\mathbb{E} | Z_1 - Z_2 |$?

Now, we could just jump in and try using 2D LOTUS, but let's take a step back and recall what we have seen earlier in Lecture 14 about the _iinearity of Normals_, and see if we can apply what we know about MGFs.

## Theorem: If $X \sim \mathcal{N}(\mu_1, \sigma_1^2)$ and  $Y \sim \mathcal{N}(\mu_2, \sigma_2^2)$ are *independent*, then $X + Y \sim \mathcal{N}(\mu_1 + \mu_2, \sigma_1^2 + \sigma_2^2)$

Recall from Lecture 17 that the MGF of a sum of independent r.v. is just the product of their respective MGF.

\begin{align}
  M(X + Y) &= \mathbb{E}(e^{t(X+Y)}) \\
           &= \mathbb{E}(e^{tX}) \, \mathbb{E}(e^{tY}) \\
           &= \mathbb{E}(e^{\mu_1 t + \frac{1}{2} \sigma_1^2 t^2 }) \, \mathbb{E}(e^{\mu_2 t + \frac{1}{2} \sigma_2^2 t^2 }) \\
           &= \mathbb{E} \left( e^{(\mu_1 + \mu_2) t + \frac{1}{2} (\sigma_1^2 + \sigma_2^2) t^2 } \right) &\quad \blacksquare
\end{align}

Returning now to our original question, we want to find $\mathbb{E} |Z_1 - Z_2|$, with $Z_1, Z_2 \sim \mathcal{N}(0,1)$.

Keep in mind that $Z_1 - Z_2 \sim \mathcal{N}(0, 2)$. But since this is just the standard normal scaled with $\sqrt{2}$, we can do this:

\begin{align}
  \mathbb{E} | Z_1 - Z_2 | &= \mathbb{E} | \sqrt{2} Z | & \quad \text{, where } Z \sim \mathcal{N}(0,1) \\
  &= \sqrt{2} \mathbb{E}|Z| \\
  &= \sqrt{2} \int_{-\infty}^{\infty} |z| \, \frac{1}{\sqrt{2 \pi}} \, e~{-\frac{z^2}{2}} \, dz \\
  &= \sqrt{\frac{2}{\pi}}
\end{align}

## Multinomial Distribution

A generalization of the binomial distribution, $Mult(n, \vec{p})$ has parameter $n$ for number of items observed, with probability vector $\vec{p} = p_1, p_2, \dots , p_k)$ such that $p_j \ge 0$ and $\sum{p_i} = 1$

So while the binomial has 2 classes, with success equating to $p$ and failure equating to $q$, the multinomial has $k$ classes, each with respective probability $p_i$.

We have $n$ objects which we are independently putting into $k$ categories.

\begin{align}
   \vec{X} &= Mult(n, \vec{p}) \quad \text{, with } \vec{X} = ( X_1, X_2, \dots , X_k) \\
   \\
   P_j &= P(\text{category }j) \\
   \\
   X_j &= \text{number of objects in category } j
\end{align}

#### Joint PMF of Multinomial

Because this is a _joint distribution_, $Mult(n, \vec{p})$ has a joint PMF.

\begin{align}
  P(X_1=n_1, X_2=n_2, \dots , X_k=n_k) &= \frac{n!}{n_{1}!n_{2}! \dots n_{k}!} \, p_1^{n_1} p_2^{n_2} \dots p_k^{n_k}
\end{align}

#### Marginal distribution $X_j$ of Multinomial

Given $\vec{X} \sim Mult_k(n, \vec{p})$, what is the marginal distribution of $X_j$?

Well, since we are interested in only the two cases of an object being in class $k$ or not, this is _binomial_.

So for the marginal distribution $X_j$ of $Mult$,

\begin{align}
  &X_j \sim Bin(n, p_j) \\
  \\
  &\mathbb{E}(X_j) = n \, p_j \\
  \\
  &\mathbb{Var}(X_j) = n \, p_j \, (1 - p_j)
\end{align}

#### Lumping Property

Given a situation where there are n voters in a population sample, and there are 10 political parties, $\vec{X} = (X_1, X_2, \dots, X_10) \sim Mult(n, (p_1, p_2, \dots, p_10))$...

Let us say that political parties 3 through 10 are relatively minor compared to parties 1 and 2. We can describe this case where we want to _lump together_ parties 3 through 10 with another multinomial $Y$ such that:

\begin{align}
  \vec{Y} = (Y_1, Y_2, Y_{3, \dots,10}) \sim Mult(n, (p_1, p_2, p_{3, \dots , 10}))
\end{align}

Here, we gather up the counts/probabilities for parties 3 through 10, and now we have a multinomial with essentially three classes.

#### Conditional Joint PMF

Say that we have a k-class $\vec{X} \sim Mult_k(n, \vec{p})$, but we know $X_1 = n_1$, but we don't know about the other $k-1$ classes...

Given $X_1 = n_1$

\begin{align}
  (X_2, \dots , X_k) &\sim Mult_{k-1}(n-n_1, (p'_2, \dots , p'_k )) & \quad \text{ where } p'_j = P(\text{in class j} | \text{not in class }1) \\
  &\sim Mult((n - n_1), (\frac{p_2}{1-p_1}, \dots , \frac{p_k}{1-p_1})) & \quad \text{ or alternatively... } \\
  &\sim Mult((n - n_1), (\frac{p_2}{p_2 + \dots + p_k}, \dots , \frac{p_k}{p_2 + \dots + p_k})) & \quad \text{ or alternatively... }
\end{align}

All we are doing here is simply re-normalizing the probability vector to take into account that we have information that $X_1 = n_1$. Multinomials are simple and intuitive!

## The Cauchy Interview Problem

### Or a good example of working with a continuous joint PDF

The Cauchy Distribution is $T = \frac{X}{Y}$ with $X, Y$ i.i.d. $\mathcal{N}(0,1)$.

It looks simple enough and appears to be quite useful, but it does have some weird properties.

* it has no mean
* it has no variance
* it defies the Law of Large Numbers; averaging up a bunch of Cauchy will not yield a normal distribution, but remains Cauchy!

_Can you find the PDF of T_?

#### Using double integrals

Let us try to find the CDF, and derive the PDF from that.

\begin{align}
  \text{CDF: }P(\frac{X}{Y} \le t) &= P\left(\frac{X}{|Y|}\right) & \quad \text{ following from the symmetry of the Normal} \\
  &= P(X \le t \, |Y| ) \\
  &= \int_{-\infty}^{\infty} \int_{-\infty}^{t|y|} \frac{1}{\sqrt{2\pi}} e^{-\frac{x^2}{2}} \, \frac{1}{\sqrt{2\pi}} e^{-\frac{y^2}{2}} \, dx \, dy \\
  &= \frac{1}{\sqrt{2\pi}} \, \int_{-\infty}^{\infty} e^{-\frac{y^2}{2}} \, \int_{-\infty}^{t|y|} \frac{1}{\sqrt{2\pi}} e^{-\frac{x^2}{2}} \, dx \, dy & \quad \text{ starting from } x \\
  &= \frac{1}{\sqrt{2\pi}} \, \int_{-\infty}^{\infty} e^{-\frac{y^2}{2}} \, \Phi(t|y|) \, dy & \quad \text{but we have an even function} \\
  &= \sqrt{ \frac{2}{\pi}} \, \int_{0}^{\infty} e^{-\frac{y^2}{2}} \, \Phi(t|y|) \, dy \\
  \\
  \text{PDF: } F'(t) &= \sqrt{\frac{2}{\pi}} \int_{0}^{\infty} y \, e^{-\frac{y^2}{2}} \, \frac{1}{\sqrt{2\pi}} \, e^{-\frac{t^2y^2}{2}} \, dy \\
  &= \frac{1}{\pi} \int_{0}^{\infty} y \, e^{-\frac{(1+t^2)y^2}{2}} \, dy & \quad \text{and with } u = \frac{(1+t^2)y^2}{2} \text{, } du = (1+t^2) \, y \, dy  \\
  &= \frac{1}{\pi} \int_{0}^{\infty} \frac{(1 + t^2)}{(1 + t^2)} \, y \, e^{-\frac{(1+t^2)y^2}{2}} \, dy \\
  &= \frac{1}{\pi} \int_{0}^{\infty} \frac{1}{1 + t^2} \, e^{-u} \, du \\
  &= \boxed{ \frac{1}{\pi (1+t^2)} } & \quad \text{ and just integrate this to get the CDF} \\
  \\
  \text{CDF: } &= \boxed{ \frac{tan^{-1}(t)}{\pi} }
\end{align}

#### Using the Law of Total Probability

Recall that the PDF of $\mathcal{N}(0,1)$ is $\phi(y)$

\begin{align}
  P(X \le t|Y|) &= \int_{-\infty}^{^infty} P\left(X \le t \lvert Y \rvert \mid Y=y\right) \, \phi(y) \, dy  \\
  &= \int_{-\infty}^{\infty} \Phi \left(t|y\right) \, \phi(y) \, dy & \quad \text{ which is pretty much what we did above }
\end{align}


```python
# Import packages that are relevant for the exam.
import numpy as np
from scipy import optimize
import matplotlib.pyplot as plt
import sympy as sm
from mpl_toolkits.mplot3d import Axes3D
from colorama import Fore
from colorama import Style
import ipywidgets as widgets
import matplotlib
```

# 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).

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

**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.

## Question 1.1

We will start by defining a function which returns the maximum utility level an individual can obtain given the level of human capital, $h$, the wage, $w$, the benifit level, $b$, and the parameters $\rho$ and $\gamma$. The function will further return the binary variable for weather an individual will work or not and the minimum level of human captial for which an individual will work. We will calculate the utility level if an individual work in the second period and utility level if an individual do not work as well. The size of these utilities determines if the individual will work or not as the individual will only work if the utility from working is higher than not working.

```python
# Define the function
def utility(h, w, b, rho, gamma):

    # 1) Define the consumption given the level of human capital and the wage
    c = w*h*1

    # 2) Calculate the utility if the individual works
    v_e = c ** (1 - rho) / (1 - rho) - gamma * 1

    # 3) Calculate the utility if the individual do not work
    v_u = b ** (1 - rho) / (1 - rho)

    # 4) Return the maximum utility between working and not working
    v_max = np.maximum(v_e, v_u)

    # 5) If the individual works then the maximum utility will be different from the utility when working.
    #    We will use this to create a boolean which tells whether the individual is working or not
    boole = v_max!=v_u

    # 6) Convert the boolean to an binary taking the value 1 if the individual is working.
    #    We use this to plot the individuals who are working
    working = 1*boole

    # 7)  Find the lowest value (threshold) of human capital for which an individual will work.
    bounds = [(0.0001, None), (-0.9999, 0.9999), (0.0001, None)]

    h_star = optimize.minimize(
    lambda h_t:  ((w*h_t)**(1 - rho) / (1 - rho) - gamma*1 - v_u)**2, 1)
    h_star2 = h_star.x

    return v_max, working, h_star2
```

We will use the function defined above to find out who which value of human capital an individual will work in the second period. We do this for the relevant levels of human capital defined above (h_vec) and the given parameters above.

```python
# 1) Calulate the maximum utilities and the decision weather to work or not for different levels of human capital.
optimum2 = utility(h_vec, w, b, rho, gamma)

# 2) Extract the array with the maximum utilities from the return typle from utility function.
utility_opt2 = optimum2[0]

# 3) Extract the array with the decision whether to work or not from the return typle from utility function.
work_opt2 = optimum2[1]
```

We will now plot the labor supply as a function of the human capital level. We will use the relevant levels of human capital defined above (h_vec).

```python
# 1) Devide the relevant levels of human captial between the values below and above the human capital threshold for working
h_vec_low = [i for i in h_vec if i <= optimum2[2]]
h_vec_high = [i for i in h_vec if i > optimum2[2]]

# 2) Devide the decision to work between the values below and above the human capital threshold for working
work_opt2_low = [i for i in work_opt2 if i <= 0]
work_opt2_high = [i for i in work_opt2 if i > 0]

# 3) Plot the labor supply as a function of the human capital level and the threshold for working
plt.style.use('ggplot')
fig = plt.figure(figsize=(12,8))
plt.plot(h_vec_low, work_opt2_low, color='b')
plt.axvline(x=optimum2[2], linewidth=1, color='r', linestyle='dashed')
plt.plot(h_vec_high, work_opt2_high, color='b')
print(f'The threshold for working is: {optimum2[2][0]:.3f}')
plt.ylabel('Labor supply')
plt.xlabel('Level of human capital')
plt.legend(['Labor supply','Threshold for working'])
plt.show()
```

As we can see from the graph above, the individuals will work in period 2 if their human capital level is above 0.55. We will now present the results from above in a graph with the level of human capital on the x-axis and the utility level on the y-axis.

```python
# 1) plot the utility level as a function of the level of human capital and plot the threshold for working
plt.figure(figsize=(12,8))
plt.plot(h_vec, utility_opt2, color='b')
plt.axvline(x=optimum2[2], linewidth=1, color='r', linestyle='dashed')

# 2) Change the axis titles and legend
plt.ylabel('Utility')
plt.xlabel('Level of human capital')
plt.legend(['Utility level','Threshold for working'])

# 3) show the graph
plt.show()
```

The graph above shows that the utility level is equal to -1 for all individuals with a human capital level below 0.55. The utility level for individuals above 0.55 is an increasing concave function with a utility of at least -1.

## Question 1.2

In the problem we will use the same approach as in question 1.1. The only difference is that we will include the expected utility in period 2.

```python
# Define the function
def utility2(h_1, w, b, rho, gamma, beta, Delta):

    # 1) Define the wage given the level of human capital and the wage
    c = w*h_1*1

    # 2) Define the expected level of human capital in period 2 when working and not working in period 1 given the level of human capital in period 1
    h_2_e = h_1 + 1 + Delta * 0.5
    h_2_u = h_1 + 0 + Delta * 0.5

    # 3) Define expected utility in period 2 for both when working and when not working in period 1
    u_2_m = utility(h_2_e, w, b, rho, gamma)
    u_2_e = u_2_m[0]

    u_2_n = utility(h_2_u, w, b, rho, gamma)
    u_2_u = u_2_n[0]

    # 4) Calculate the utility if the individual works in period 1
    v_e = c**(1 - rho) / (1 - rho) - gamma*1 + beta * u_2_e

    # 5) Calculate the utility if the individual do not work in period 1
    v_u = b**(1 - rho) / (1 - rho) + beta * u_2_u

    # 6) Return the maximum utility between working and not working
    v_max = np.maximum(v_e, v_u)

    # 7) If the individual works then the maximum utility will be different from the utility when working.
    #    We will use this to create a boolean which tells whether the individual is working or not
    boole = v_max!=v_u

    # 8) Convert the boolean to an binary taking the value 1 if the individual is working.
    #    We use this to plot the individuals who are working
    working = 1*boole

    # 9)  Find the lowest value (threshold) of human capital for which an individual will work.
    h_star = optimize.minimize(
    lambda h_t: ((w*h_t)**(1 - rho) / (1 - rho) - gamma*1 + beta * utility(h_t+1+0.5*Delta, w, b, rho, gamma)[0] - b**(1 - rho) / (1 - rho) - beta * utility(h_t+0+0.5*Delta, w, b, rho, gamma)[0])**2,1)
    h_star2 = h_star.x

    return v_max, working, h_star2
```

```python
# 1) Calulate the maximum utilities and the decision weather to work or not for different levels of human capital.
optimum1 = utility2(h_vec, w, b, rho, gamma, beta, Delta)

# 2) Devide the decision to work between the values below and above the human capital threshold for working
utility_opt1 = optimum1[0]

# 3) Plot the labor supply as a function of the human capital level and the threshold for working
work_opt1 = optimum1[1]
```

We will now plot the labor supply as a function of the human capital level. We will use the relevant levels of human capital defined above (h_vec)

```python
# 1) Devide the relevant levels of human captial between the values below and above the human capital threshold for working
h_vec_low = [i for i in h_vec if i <= optimum1[2]]
h_vec_high = [i for i in h_vec if i > optimum1[2]]

# 2) Devide the decision to work between the values below and above the human capital threshold for working
work_opt1_low = [i for i in work_opt1 if i <= 0]
work_opt1_high = [i for i in work_opt1 if i > 0]

# 3) Plot the labor supply as a function of the human capital level and the threshold for working
plt.figure(figsize=(12,8))
plt.plot(h_vec_low, work_opt1_low, color='b')
plt.axvline(x=optimum1[2], linewidth=1, color='r', linestyle='dashed')
plt.plot(h_vec_high, work_opt1_high, color='b')
print(f'The threshold for working is: {optimum1[2][0]:.3f}')
plt.ylabel('Labor supply')
plt.xlabel('Level of human capital')
plt.legend(['Labor supply','Threshold for working'])
plt.show()
```

As we can see from the graph above, the individuals will work in period 1 if their human capital level is above 0.35. We will now present the results from above in a graph with the level of human capital on the x-axis and the utility level on the y-axis.

```python
# 1) plot the utility level as a function of the level of human capital and plot the threshold for working
plt.figure(figsize=(12,8))
plt.plot(h_vec, utility_opt1, color='b')
plt.axvline(x=optimum1[2], linewidth=1, color='r', linestyle='dashed')

# 2) Change the axis titles and legend
plt.ylabel('Utility')
plt.xlabel('Level of human capital')
plt.legend(['Utility level','Threshold for working'])

# 3) show the graph
plt.show()
```

The graph above shows that the utility level is close to -2 for all individuals with a human capital level below 0.35. The utility level for individuals above 0.35 is an increasing concave function with a utility of at least the utility level of those not working.

## Question 1.3

The worker will never work if the benefits, $b$, are higher than the potential wage, $wh$, in period 2 as

$$ \frac{b^{1-\rho}}{(1-\rho)} > \frac{(wh)^{1-\rho}}{(1-\rho)}-\gamma \, \, \text{ for } b>wh \, \, \text{and } \gamma>0$$

Becasue $\gamma>0$ the worker will actually thoose not to work if the potential wage is just above the unemployment benefits because she gets disutility from working. We will show this graphically by plotting the unemployment benefits and the potential wage as a function of the human capital level and include the threshold for working, which we found in problem 1.1

```python
# 1) Define a function which return the potential wage as a function of the level of human capital
def pot_earnings(h):
    return (w*h)**(1 - rho) / (1 - rho)

# 2) Define a function which returns the unemployment benefits as a funciton of human capital
def benifit(h):
    return (h/h) * b ** (1 - rho) / (1 - rho)

# 3) Plot the potential wage, the unemployment benefit and the threshol for working
plt.figure(figsize=(12,8))
plt.plot(h_vec, pot_earnings(h_vec), 'g-')
plt.plot(h_vec, benifit(h_vec), 'orange')
plt.axvline(x=optimum2[2], linewidth=1, color='r', linestyle='dashed')

# 4) Add labels
plt.xlabel('Human capital level')
plt.ylabel('Potential earnings / benefit')

# 5) Add different colors for different working decisions based on the potential wage relative to the unemployment benefit
plt.axvspan(h_vec[0], b/w, facecolor='r', alpha=0.1)
plt.axvspan(b/w, optimum2[2], facecolor='b', alpha=0.1)
plt.axvspan(optimum2[2], h_vec[-1], facecolor='g', alpha=0.1)
axes = plt.gca()
axes.set_ylim([-5,0])
axes.set_xlim([h_vec[0],1.5])

# 6) Add ledend and show the graph
plt.legend(['Potential earnings','Unemployment benefit','Threshold for working','Not working, b>w*h', 'Not working, b<w*h', 'Working, b<w*h'])
plt.show()
```

In the first period the worker will for some levels of human capital thoose to work even though the potential wage is below the unemployment benefits. This is due to the human capital accumulation the worker gets if she decides to work which will increse the potential wage in the second period. We will show this in the same way as we showed for period 2.

```python
# 1) Plot the potential wage, the unemployment benefit and the threshol for working
plt.figure(figsize=(12,8))
plt.plot(h_vec, pot_earnings(h_vec), 'g-')
plt.plot(h_vec, benifit(h_vec), 'orange')
plt.axvline(x=optimum1[2], linewidth=1, color='r', linestyle='dashed')

# 2) Add labels
plt.xlabel('Human capital level')
plt.ylabel('Potential earnings / benefit')

# 3) Add different colors for different working decisions based on the potential wage relative to the unemployment benefit
plt.axvspan(h_vec[0], optimum1[2], facecolor='r', alpha=0.1)
plt.axvspan(optimum1[2], b/w, facecolor='y', alpha=0.1)
plt.axvspan(b/w, h_vec[-1], facecolor='g', alpha=0.1)
axes = plt.gca()
axes.set_ylim([-5,0])
axes.set_xlim([h_vec[0],1.5])

# 4) Add ledend and show the graph
plt.legend(['Potential earnings','Unemployment benefits','Threshold for working','Not working, b>w*b','Working, b>w*b', 'Working, b<w*b'])
print(f'The worker will work in the interval between {optimum1[2][0]:.3f} and {b/w:.3f}, even though the wage is below the benefits')
plt.show()
```

# 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}$.

**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$.

**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$?

**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['sigma_x'] = 3.492
par['sigma_c'] = 0.2
```

**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})$

**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.

**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$

## Question 2.1

We define the variables and parameters by appliying the `sympy`-package.

```python
# 1) Apply the ".init_printing"-function in order to ensure pretty printing.
sm.init_printing(use_unicode=True)

# 2) Define the parameters and variables of the models.
y_t       = sm.symbols('y_t')
y_tm1     = sm.symbols('y_t-1')
alpha_par = sm.symbols('alpha')
r_t       = sm.symbols('r_t')
v_t       = sm.symbols('v_t')
i_t       = sm.symbols('i_t')
pi_e1     = sm.symbols('pi^e_t+1')
h_par     = sm.symbols('h')
pi_t      = sm.symbols('pi_t')
b_par     = sm.symbols('b')
s_t       = sm.symbols('s_t')
s_tm1     = sm.symbols('s_t-1')
pi_e0     = sm.symbols('pi^e_t')
gamma_par = sm.symbols('gamma')
phi_par   = sm.symbols('phi')
pi_em0    = sm.symbols('pi^e_t-1')
pi_tm1    = sm.symbols('pi_t-1')
```

We know that the equilibrium of output gap and the equilibrium of the inflation gap is given at the point where the AD-curve and the SRAS-curve intersect. Thus, we define the AD-curve and the SRAS-curve by the parameters and variables of the given models.

Afterwards, we isolate for $\pi_t$ in the SRAS-curve and substitute the expression into the AD-curve. When this is done, we find the equilibrium of the output gap ($y^*$) by isolating $y_t$.

```python
# 1) Define the AD-curve and the SRAS-curve.
AD = sm.Eq(pi_t, 1 / (h_par*alpha_par) * (v_t - (1+ b_par * alpha_par) * y_t))
SRAS = sm.Eq(pi_t, pi_tm1 + gamma_par * y_t - phi_par * gamma_par * y_tm1 + s_t - phi_par * s_tm1)

# 2) Ensure that we have isolated pi_t in the SRAS-curve.
SRAS_solve = sm.solve(SRAS, pi_t)

# 3) Substitute the SRAS-curve into the AD-curve by substituting pi_t with the expression of the SRAS-curve.
SRAS_eq_AD = AD.subs(pi_t, SRAS_solve[0])

# 4) Solve for y_t in order to find y*.
y_star = sm.solve(SRAS_eq_AD, y_t)
```

Next, we want to derive the equilibrium of the inflation gap. In order to do so, we substitute the previously found expression of the equilibrium of the output gap into the AD-curve at the place of $y_t$. Now, we have found the equilibrium of the inflation gap. The expression is simplified in the end.

```python
# 1) Insert the equilibrium of the output gap into the AD_curve.
y_insert = AD.subs(y_t, y_star[0])

# 2) Simplify the expression.
pi_simp = sm.simplify(y_insert)

# 3) Only keep the right hand side.
pi_star = sm.solve(pi_simp, pi_t)
```

We are now able to examine the equilibrium of the output gap and the equilibrium of the inflation gap as a function of the given parameters and $y_{t-1}$, $\pi _{t-1}$, $v_t$, $s_t$ and $s_{t-1}$.

```python
# Print the equilibrium of the output gap (with text in front)
print('The equilibrium of the output gap is given by \n')
y_star
```

```python
# Print the equilibrium of the inflation gap (with text in front)
print('The equilibrium of the inflation gap is given by \n')
pi_star
```

## Question 2.2

In order to find and illustrate the equilibrium values of output and inflation, we must find the values for which the AD-curve and the SRAS-curve intersect. We use the previously calculated analytically equilibrium in order to determine the numerical eqiulibrium. However, first we insert the values of the variables in the same dictionary as before (par), i.e. $y_{t-1} = \pi_{t-1} = v_t = s_t = s_{t-1} = 0$.

```python
# Define values of the given variables.
par['y_t-1'] = 0
par['pi_t-1'] = 0
par['v_t'] = 0
par['s_t'] = 0
par['s_t-1'] = 0
```

Next, we define the equilibrium of the output gap and the equilibrium of the inflation gap as a python functions and plug in the given values of the variables and of the parameters.

```python
# 1) Define the equilibrium of the output gap as a python function.
y_eq_func = sm.lambdify((alpha_par, gamma_par, h_par, phi_par, y_tm1, s_tm1, pi_tm1, s_t, v_t, b_par), y_star[0])

# 2) Insert the values of the variables of the parameters and variables into the function.
y_eq_q2 = y_eq_func(par['alpha'], par['gamma'], par['h'], par['phi'], par['y_t-1'], par['s_t-1'], par['pi_t-1'], par['s_t'], par['v_t'], par['b'])

# 3) Define the equilibrium of the inflation gap as a python function.
pi_eq_func = sm.lambdify((alpha_par, gamma_par, h_par, y_tm1, phi_par, pi_tm1, s_tm1, v_t, b_par, s_t), pi_star[0])

# 4) Insert the values of the variables of the parameters and variables into the function.
pi_eq_q2 = pi_eq_func(par['alpha'], par['gamma'], par['h'], par['y_t-1'], par['phi'], par['pi_t-1'], par['s_t-1'], par['v_t'], par['b'], par['s_t'])
```

We are now able to examine the equilibrium of the output gap and the equilibrium of the inflation gap.

```python
# Print the equilibrium.
print('The equilibrium is given by (y_t, pi_t) = (' + str(round(y_eq_q2,3)) + ', ' + str(round(pi_eq_q2,3)) +')')
```

We can plot the SRAS-curve and the AD-curve in a graph. However, first we need to define the function of the SRAS-curve and the function of the AD-curve. Furthermore, we show the equilibrium in the graph.

```python
# 1) Isolate pi_t in the SRAS-function.
SRAS_solve = sm.solve(SRAS, pi_t)

# 2) Turn the SRAS-curve into a python function.
SRAS_func = sm.lambdify((gamma_par, phi_par, y_tm1, y_t, s_tm1, pi_tm1, s_t), SRAS_solve[0])

# 3) Isolate pi_t in the AD-curve.
AD_solve = sm.solve(AD, pi_t)

# 4) Turn the AD-curve into a python function.
AD_func = sm.lambdify((alpha_par, b_par, y_t, v_t, h_par), AD_solve[0])

# 5) Define some values of the x-axis.
y_arange = np.arange(-1, 1, 0.01)

# 6) Plot the functions.
plt.figure(figsize=(12,8))
plt.plot(y_arange, AD_func(par['alpha'], par['b'], y_arange, par['v_t'], par['h']))
plt.plot(y_arange, SRAS_func(par['gamma'], par['phi'], par['y_t-1'], y_arange, par['s_t-1'], par['pi_t-1'], par['s_t']))
plt.plot(y_eq_q2, pi_eq_q2, 'ro')
plt.ylabel('Inflation gap')
plt.xlabel('Output gap')
plt.legend(['AD-curve','SRAS-curve', 'Equilibrium'])
axes = plt.gca()
axes.set_ylim([-0.1,0.1])
axes.set_xlim([-0.1,0.1])
plt.show()
print('The equilibrium is given by (y_t, pi_t) = (' + str(round(y_eq_q2,3)) + ', ' + str(round(pi_eq_q2,3)) +')')
```

We consider a situation where $v_t=0.1$. We define a new variable for $v_t$, and use the functions defined before to examine the difference.

```python
# 1) Define a new variable for v_t.
par['v_t_new'] = 0.1

# 2) Examine the new eqilibrium by inserting the new value of v_t.
y_eq_q2_new = y_eq_func(par['alpha'], par['gamma'], par['h'], par['phi'], par['y_t-1'], par['s_t-1'], par['pi_t-1'], par['s_t'], par['v_t_new'], par['b'])
pi_eq_q2_new = pi_eq_func(par['alpha'], par['gamma'], par['h'], par['y_t-1'], par['phi'], par['pi_t-1'], par['s_t-1'], par['v_t_new'], par['b'], par['s_t'])

# 3) Print the equilibrium.
print('The equilibrium is given by (y_t, pi_t) = (' + str(round(y_eq_q2_new, 3)) + ', ' + str(round(pi_eq_q2_new, 3)) +')')
```

Again, we examine this on a graph.

```python
# Plot the functions.
plt.figure(figsize=(12,8))
plt.plot(y_arange, AD_func(par['alpha'], par['b'], y_arange, par['v_t_new'], par['h']))
plt.plot(y_arange, SRAS_func(par['gamma'], par['phi'], par['y_t-1'], y_arange, par['s_t-1'], par['pi_t-1'], par['s_t']))
plt.plot(y_eq_q2_new, pi_eq_q2_new, 'ro')
plt.ylabel('Inflation gap')
plt.xlabel('Output gap')
plt.legend(['AD-curve','SRAS-curve', 'Equilibrium'])
axes = plt.gca()
axes.set_ylim([-0.1,0.1])
axes.set_xlim([-0.1,0.1])
plt.show()
print('The equilibrium is given by (y_t, pi_t) = (' + str(round(y_eq_q2_new, 3)) + ', ' + str(round(pi_eq_q2_new, 3)) +')')
```

## Question 2.3

We want to examine how the economy evolves when $v_t$ and $s_t$ are given by AR(1)-processes. We consider a situation where a demand shock hits the economy in period 0. The shock is only present in period 0.

In order to examine the effect of a shock on the output gap and the inflation gap, we create a vector of $x_t$, $c_t$, $v_t$ and $s_t$. We use these vector to generate a vector of $y_t$ and a vector of $\pi_t$. The vectors of $y_t$ and $\pi_t$ are used to plot the evolution of the economy.

```python
# 1) Define length of period.
total = 1000

# 2) Define empty lists.
t_list = np.empty(total)
x_t_list = np.empty(total)
c_t_list = np.empty(total)
v_t_list = np.empty(total)
s_t_list = np.empty(total)
y_t_list = np.empty(total)
pi_t_list = np.empty(total)

# 3) Define starting parameters.
start = {}
start['y_n'] = 0
start['pi_n'] = 0
start['v_n'] = 0
start['s_n'] = 0

# 4) Define the length of the time vector.
for i in range (0, total):
    t_list[i] = i

# 5) Set the values in the first period.
    if t_list[i] == 0:
        c_t_list[i] = 0
        x_t_list[i] = 0.1
        v_t_list[i] = par['delta'] * start['v_n'] + x_t_list[i]
        s_t_list[i] = par['omega'] * start['s_n'] + c_t_list[i]
        y_t_list[i] = y_eq_func(par['alpha'], par['gamma'], par['h'], par['phi'], start['y_n'], start['s_n'], start['pi_n'], s_t_list[i], v_t_list[i], par['b'])
        pi_t_list[i] = pi_eq_func(par['alpha'], par['gamma'], par['h'], start['y_n'], par['phi'], start['pi_n'], start['s_n'], v_t_list[i], par['b'], s_t_list[i])

# 6) Set the values in the following periods.
    else:
        c_t_list[i] = 0
        x_t_list[i] = 0
        v_t_list[i] = par['delta'] * v_t_list[i-1] + x_t_list[i]
        s_t_list[i] = par['omega'] * s_t_list[i-1] + c_t_list[i]
        y_t_list[i] = y_eq_func(par['alpha'], par['gamma'], par['h'], par['phi'], y_t_list[i-1], s_t_list[i-1], pi_t_list[i-1], s_t_list[i], v_t_list[i], par['b'])
        pi_t_list[i] = pi_eq_func(par['alpha'], par['gamma'], par['h'], y_t_list[i-1], par['phi'], pi_t_list[i-1], s_t_list[i-1], v_t_list[i], par['b'], s_t_list[i])
```

We are now able to examine the evolution of the economy when a demand shock occurs. In the first graph, we consider the change in the SRAS-curve and the change in the AD-curve from period 0 to period 1 (the shock does affect the economy already in period 0). In the second graph, we examine the evolution of the output gap and the evolution of the inflation gap as a function of time.

```python
# Plot the functions.
plt.figure(figsize=(12,8))
plt.plot(y_arange, AD_func(par['alpha'], par['b'], y_arange, par['v_t'], par['h']),color='r',linestyle='dashed')
plt.plot(y_arange, AD_func(par['alpha'], par['b'], y_arange, v_t_list[0], par['h']),color='r')
plt.plot(y_arange, SRAS_func(par['gamma'], par['phi'], start['y_n'], y_arange, start['s_n'], start['pi_n'], s_t_list[0]),color='b')
plt.plot(y_t_list[0], pi_t_list[0], 'ro',color='r')
plt.ylabel('Inflation gap')
plt.xlabel('Output gap')
plt.plot(y_arange, AD_func(par['alpha'], par['b'], y_arange, v_t_list[1], par['h']),color='r',linestyle=':')
plt.plot(y_arange, SRAS_func(par['gamma'], par['phi'], y_t_list[0], y_arange, s_t_list[0], pi_t_list[0], s_t_list[1]),color='b',linestyle=':')
plt.plot(y_t_list[1], pi_t_list[1], 'ro', color='b')
plt.legend(['AD stady state', 'AD-curve, $p_0$','SRAS-curve, steady state and $p_0$', 'Equilibrium, $p_0$', 'AD-curve, $p_1$','SRAS-curve, $p_1$', 'Equilibrium, $p_1$'])
axes1 = plt.gca()
axes1.set_ylim([-0.05,0.05])
axes1.set_xlim([-0.05,0.05])
plt.show()
```

```python
# Plot the functions.
plt.figure(figsize=(12,8))
plt.plot(t_list, y_t_list)
plt.plot(t_list, pi_t_list)
plt.ylabel('Output gap and inflation gap')
plt.xlabel('Time')
plt.legend(['Output gap','Inflation gap'])
axes_q3 = plt.gca()
axes_q3.axhline(y=0, color='black')
axes_q3.set_ylim([-0.005,0.025])
axes_q3.set_xlim([0,100])
plt.show()
```

In the first graph, we note that the AD-curve moves upwards in period 0 due to the demand shock. However, since supply initially is unaffected by the demand shock, the SRAS-curve is unchanged from the SRAS-curve in steady state. This generates a boom in the economy with a positive output gap and a positive inflation gap.
In period 1, the SRAS-curve moves up as a response so the boom in the previous period. The AD-curve moves downwards, since the demand shocks is no longer present. Hence, the output gap decreases from period 0 to period 1 while the inflation gap increases from period 0 to period 1. This tendency would continue, so at a point the output gap will be negative.

We note this on the second graph, where the output gap decreases from period 0 and onwards until it converges back to steady state. The inflation gap increases until it too converges back to steady state. We note that the economy is back in steady state in approximately period 75.

## Question 2.4

Now, we assume that $x_t$ and $c_t$ are stocastic shocks with a standard deviation of $\sigma$ and a mean of $0$. This implies that the expected shock in period $t$ is $0$, but that a shock can occur in every period. In order to show the evolution of the economy and derive the moments, we use almost the same structure as before, but with a new definition of $x_t$ and $c_t$.

```python
# 1) Define length of period.
total_q4 = 1000

# 2) Define empty lists.
t_list_q4 = np.empty(total_q4)
x_t_list_q4 = np.empty(total_q4)
c_t_list_q4 = np.empty(total_q4)
v_t_list_q4 = np.empty(total_q4)
s_t_list_q4 = np.empty(total_q4)
y_t_list_q4 = np.empty(total_q4)
pi_t_list_q4 = np.empty(total_q4)

# 3) Define starting parameters.
start = {}
start['y_n'] = 0
start['pi_n'] = 0
start['v_n'] = 0
start['s_n'] = 0

# 4) Define the length of the time vector.
for i in range (0, total_q4):
    t_list_q4[i] = i

# 5) Set a seed number and draw a random number from the normal distribution with mean 0 and variance sigma.
    np.random.seed(117)
    c_t_list_q4_old = np.random.normal(loc=0, scale=par['sigma_c'], size=(total_q4, 1))
    c_t_list_q4 = c_t_list_q4_old[:,0]
    x_t_list_q4_old = np.random.normal(loc=0, scale=par['sigma_x'], size=(total_q4, 1))
    x_t_list_q4 = x_t_list_q4_old[:,0]

# 6) Set the values in the first period.
    if t_list_q4[i] == 0:
        v_t_list_q4[i] = par['delta'] * start['v_n'] + x_t_list_q4[i]
        s_t_list_q4[i] = par['omega'] * start['s_n'] + c_t_list_q4[i]
        y_t_list_q4[i] = y_eq_func(par['alpha'], par['gamma'], par['h'], par['phi'], start['y_n'], start['s_n'], start['pi_n'], s_t_list_q4[i], v_t_list_q4[i], par['b'])
        pi_t_list_q4[i] = pi_eq_func(par['alpha'], par['gamma'], par['h'], start['y_n'], par['phi'], start['pi_n'], start['s_n'], v_t_list_q4[i], par['b'], s_t_list_q4[i])

# 7) Set the values in the following periods.
    else:
        v_t_list_q4[i] = par['delta'] * v_t_list_q4[i-1] + x_t_list_q4[i]
        s_t_list_q4[i] = par['omega'] * s_t_list_q4[i-1] + c_t_list_q4[i]
        y_t_list_q4[i] = y_eq_func(par['alpha'], par['gamma'], par['h'], par['phi'], y_t_list_q4[i-1], s_t_list_q4[i-1], pi_t_list_q4[i-1], s_t_list_q4[i], v_t_list_q4[i], par['b'])
        pi_t_list_q4[i] = pi_eq_func(par['alpha'], par['gamma'], par['h'], y_t_list_q4[i-1], par['phi'], pi_t_list_q4[i-1], s_t_list_q4[i-1], v_t_list_q4[i], par['b'], s_t_list_q4[i])

```

We simulate the model for 1,000 periods. We note that the output gap and the inflation gap are very volatile when a shock can hit the economy in every period.

```python
# Plot the graph.
plt.figure(figsize=(12,8))
plt.plot(t_list_q4, y_t_list_q4)
plt.plot(t_list_q4, pi_t_list_q4)
plt.ylabel('Output gap and inflation gap')
plt.xlabel('Time')
axes_q4 = plt.gca()
axes_q4.axhline(y=0, color='black')
plt.legend(['Output gap','Inflation gap'])
plt.show()
```

We are asked to calculate specific moments. These are e.g. the auto-correlation of the output gap and auto-correlation of the inflation gap. In order to derive those, we must define the values of the lagged output gap and the lagged inflation gap.

```python
# 1) Define empty lists.
y_tm1_list_q4 = np.empty(total_q4)
pi_tm1_list_q4 = np.empty(total_q4)

# 2) Define starting parameters.
y_tm1_list_q4[0] = start['y_n']
pi_tm1_list_q4[0] = start['pi_n']

# 3) Lag output gap and inflation gap in the following periods.
for k in range (1, total_q4):
    if t_list_q4[i] > 0:
        y_tm1_list_q4[k] = y_t_list_q4[k-1]
        pi_tm1_list_q4[k] = pi_t_list_q4[k-1]

```

This moments to be calculated are
- The variance of the output gap, $var(y_t)$
- The variance of the inflation gap, $var(\pi_t)$
- The correlation between the output gap and the inflation gap, $corr(y_t, \pi_t)$
- The auto-correlation between the output gap and the lagged output gap, $corr(y_t, y_{t-1})$
- The auto-correlation between the inflation gap and the lagged inflatio gap, $corr(\pi_t, \pi_{t-1})$

This is done by using the functions of the `numpy` package.

```python
# 1) Calculate the variance of the output gap.
variance_y = np.var(y_t_list_q4)

# 2) Calculate the variance of the inflation gap.
variance_pi = np.var(pi_t_list_q4)

# 3) Calculate the correlation between the output gap and the inflation gap.
corr_y_pi_old = np.corrcoef(y_t_list_q4, pi_t_list_q4)
corr_y_pi = corr_y_pi_old[1,0]

# 4) Calculate the auto-correlation of the output gap.
auto_corr_y_old = np.corrcoef(y_t_list_q4, y_tm1_list_q4)
auto_corr_y = auto_corr_y_old[1,0]

# 5) Calculate the auto-correlation of the inflation gap.
auto_corr_pi_old = np.corrcoef(pi_t_list_q4, pi_tm1_list_q4)
auto_corr_pi = auto_corr_pi_old[1,0]

# 6) Print the results.
print('- var(y_t)           =  ' + str(round(variance_y,3)))
print('- var(pi_t)          =  ' + str(round(variance_pi,3)))
print('- corr(y_t, pi_t)    = ' + str(round(corr_y_pi,3)))
print('- corr(y_t, y_t_1)   =  ' + str(round(auto_corr_y,3)))
print('- corr(pi_t, pi_t_1) =  ' + str(round(auto_corr_pi,3)))
```

## Question 2.5

We want to plot the correlation between the output gap and the inflation gap as a function of $\phi$. In order to do this, we define a function with the content of the code produced in problem 2.4. However, the value of $\phi$ is not fixed, and we are able to change the value of $\phi$ by calling the function.

```python
# 1) Define function.
def phi_func(phi_value):
    """
    This function defines the array of every variable in our model. This ensures that we are able to determine the array
    of y_t and pi_t which are the variables of special interest in this context. First, the lenth of the period is set.
    Afterwards, an empty list for every variable is defined. In the following step, we set the starting values. Afterwards,
    we define the values in period 0 and in every subsequent period.
    In the end, the correlation between y_t and pi_t as a function of phi is calculated and returned. One can choose the
    value of phi by replacing phi_value with this value when calling the function later on.
    """

# 2) Define length of period.
    total_q5 = 1000

# 3) Define empty lists.
    t_list_q5 = np.empty(total_q5)
    x_t_list_q5 = np.empty(total_q5)
    c_t_list_q5 = np.empty(total_q5)
    v_t_list_q5 = np.empty(total_q5)
    s_t_list_q5 = np.empty(total_q5)
    y_t_list_q5 = np.empty(total_q5)
    pi_t_list_q5 = np.empty(total_q5)

# 4) Define starting parameters.
    start = {}
    start['y_n'] = 0
    start['pi_n'] = 0
    start['v_n'] = 0
    start['s_n'] = 0

# 5) Define the length of the time vector.
    for i in range (0, total_q5):
        t_list_q5[i] = i

# 6) Set a seed number and draw a random number from the normal distribution with mean 0 and variance sigma.
        np.random.seed(117)
        c_t_list_q5_old = np.random.normal(loc=0, scale=par['sigma_c'], size=(total_q5, 1))
        c_t_list_q5 = c_t_list_q5_old[:,0]
        x_t_list_q5_old = np.random.normal(loc=0, scale=par['sigma_x'], size=(total_q5, 1))
        x_t_list_q5 = x_t_list_q5_old[:,0]

# 7) Set the values in the first period.
        if t_list_q5[i] == 0:
            v_t_list_q5[i] = par['delta'] * start['v_n'] + x_t_list_q5[i]
            s_t_list_q5[i] = par['omega'] * start['s_n'] + c_t_list_q5[i]
            y_t_list_q5[i] = y_eq_func(par['alpha'], par['gamma'], par['h'], phi_value, start['y_n'], start['s_n'], start['pi_n'], s_t_list_q5[i], v_t_list_q5[i], par['b'])
            pi_t_list_q5[i] = pi_eq_func(par['alpha'], par['gamma'], par['h'], start['y_n'], phi_value, start['pi_n'], start['s_n'], v_t_list_q5[i], par['b'], s_t_list_q5[i])

# 8) Set the values in the following periods.
        else:
            v_t_list_q5[i] = par['delta'] * v_t_list_q5[i-1] + x_t_list_q5[i]
            s_t_list_q5[i] = par['omega'] * s_t_list_q5[i-1] + c_t_list_q5[i]
            y_t_list_q5[i] = y_eq_func(par['alpha'], par['gamma'], par['h'], phi_value, y_t_list_q5[i-1], s_t_list_q5[i-1], pi_t_list_q5[i-1], s_t_list_q5[i], v_t_list_q5[i], par['b'])
            pi_t_list_q5[i] = pi_eq_func(par['alpha'], par['gamma'], par['h'], y_t_list_q5[i-1], phi_value, pi_t_list_q5[i-1], s_t_list_q5[i-1], v_t_list_q5[i], par['b'], s_t_list_q5[i])

# 9) Define the correlation between y_t and pi_t
    corr_q5_old = np.corrcoef(y_t_list_q5, pi_t_list_q5)
    corr_q5 = corr_q5_old[1,0]
    return corr_q5
```

We have just defined the function. Now, we choose some values of $\phi$ between 0 and 1 and find the corresponding correlation between $y_t$ and $\pi_t$.

```python
# 1) Define the number of different values of phi that we want to find the correlation for.
dif_values = 100

# 2) Define a vector with the number of different values in the range between 0 and 1.
phi_values = np.linspace(0, 1, dif_values)

# 3) Define an empty vector with the length of the number of different values of phi where we want to find the correlation.
corr_list = np.empty(dif_values)

# 4) Find the correlation as a function of the different values of phi.
for k, phi_val in enumerate(phi_values):
    corr_list[k] = phi_func(phi_val)
```

We plot the graph in order to examine how the correlation between $y_t$ and $\pi_t$ changes with $\phi$.

We note that higher levels of $\phi$ implies a more positive correlation between $y_t$ and $\pi_t$. This makes sense if we return to the equilibrium of the inflation gap is given by ($\pi^*$) from problem 2.1. In this equation, a higher level of $\phi$ implies that $\pi_t$ is more affected by $y_{t-1}$. We found in problem 2.4 that the auto-correlatiob between $y_t$ and $y_{t-1}$ was close to 1, so the change in $\pi_t$ must be more similar to the change in $y_t$ when $\phi$ is greater.

```python
# Plot the graph.
plt.figure(figsize=(12,8))
plt.plot(phi_values, corr_list)
plt.ylabel('Correlation between y_t and pi_t')
plt.xlabel('Values of phi')
axes_q5 = plt.gca()
axes_q5.axhline(y=0, color='black')
plt.show()
```

We want to find the value of $\phi$ that ensures that the correlation between the output gap and the inflation gap is closest possible to 0.31. In order to do so, we can use the optimize function from the `scipy` package.

First, we define the desired value of correlation. Afterwards, we use the previously defined function, but withdraws the desired value of correlation in the end and squares the value. This ensures that we only consider positive values, and that we find the value of $\phi$ where correlation is closest to 0.31 by minimizing the function.

```python
# 1) Define desired level of correlation.
corr_given = 0.31

# 2) Take an initial guess.
initial_guess = 0

# 3) Define the function that should be minimized and withdraw desired level of correlation.
def min_function(phi_values):
    """ Minimizes the function with respect to phi_values. The desired level of correlation is inserted, and the expression
    is squared, so we only consider non-negative values. The value of phi that minimizes the function that ensures that
    the correlation is closest possible to the desired correlation level is returned.
    """
    return (phi_func(phi_values)-corr_given)**2

# 4) Find the value of phi that minimizes the function.
result = optimize.minimize(min_function, initial_guess)

# 5) Save value of phi that minimzes the function that ensures that correlation is closest possible to 0.31.
phi_min_old = result.x
phi_min = phi_min_old[0]

# 6) Print the value of phi that ensures that the correlation is closest possible to 0.31
print('The value of phi that ensures that the correlation between y_t and pi_t is closest to 0.31 is phi = ' + str(round(phi_min, 3)))
```

We plot the same graph as before and insert the value of $\phi$ that ensures that the level of correlation between the output gap and the inflation gap is closest possible to 0.31.

```python
# Plot the graph.
plt.figure(figsize=(12,8))
plt.plot(phi_values, corr_list)
plt.plot(phi_min, corr_given, 'ro')
plt.ylabel('Correlation between y_t and pi_t')
plt.xlabel('Values of phi')
plt.legend(['Correlation as function of phi', 'Value of phi with correlation closest to 0.31'])
axes_q5 = plt.gca()
axes_q5.axhline(y=0, color='black')
plt.show()
```

## Question 2.6

We want to find the value of $\phi$, $\sigma_x$ and $\sigma_c$ so that the model has moments that are closest possible to the US economy. First, we define the real values of the variance of $y_t$ and $\pi_t$, and the auto-correlation of $y_t$ and $\pi_t$. The correlation between $y_t$ and $\pi_t$ is defined in problem 2.5

```python
# Define real values of variance and auto-correlation.
var_y_given = 1.64
var_pi_given = 0.21
auto_y_given = 0.84
auto_pi_given = 0.48
```

We apply the same function as in 2.5, but now we want to minimize for $\phi$, $\sigma_x$ and $\sigma_c$ instead of just $\phi$. In the end, we withdraw the real values and square the parameters, so that it is the minimal value we find. We sum the five moments, so that we find the values that minimizes the sum, and hence, the values that ensures that the parameters of our model is closest possible to the US economy.

```python
# 1) Define function.
def bs_usa(opt_values):
    """ Step 3-9 follow the same procedure as the previusly defined function, "phi_func". In step 2, the parameters to be
    calibrated are combined into a list. In step 10, the variance, correlation and auto-correlation are calculated.
    In step 11, the desired levels of the moments are withdrawn and the expressions are squared in order to ensure
    that these are non-negative. In the last step, the sum of the squared moments with withdrawn values is calculated
    and returned.
    """

# 2) Combine the parameters to be calibrated into a list.
    phi_usa, sigma_x_usa, sigma_c_usa = opt_values

# 3) Define length of period.
    total_q6 = 1000

# 4) Define empty lists.
    t_list_q6 = np.empty(total_q6)
    x_t_list_q6 = np.empty(total_q6)
    c_t_list_q6 = np.empty(total_q6)
    v_t_list_q6 = np.empty(total_q6)
    s_t_list_q6 = np.empty(total_q6)
    y_t_list_q6 = np.empty(total_q6)
    pi_t_list_q6 = np.empty(total_q6)
    y_tm1_list_q6 = np.empty(total_q6)
    pi_tm1_list_q6 = np.empty(total_q6)

# 5) Define starting parameters.
    start = {}
    start['y_n'] = 0
    start['pi_n'] = 0
    start['v_n'] = 0
    start['s_n'] = 0

# 6) Define the length of the time vector.
    for i in range (0, total_q6):
        t_list_q6[i] = i

# 7) Set a seed number and draw a random number from the normal distribution with mean 0 and variance sigma.
        np.random.seed(117)
        c_t_list_q6_old = np.random.normal(loc=0, scale=sigma_c_usa, size=(total_q6, 1))
        c_t_list_q6 = c_t_list_q6_old[:,0]
        x_t_list_q6_old = np.random.normal(loc=0, scale=sigma_x_usa, size=(total_q6, 1))
        x_t_list_q6 = x_t_list_q6_old[:,0]

# 8) Set the values in the first period.
        if t_list_q6[i] == 0:
            v_t_list_q6[i] = par['delta'] * start['v_n'] + x_t_list_q6[i]
            s_t_list_q6[i] = par['omega'] * start['s_n'] + c_t_list_q6[i]
            y_t_list_q6[i] = y_eq_func(par['alpha'], par['gamma'], par['h'], phi_usa, start['y_n'], start['s_n'], start['pi_n'], s_t_list_q6[i], v_t_list_q6[i], par['b'])
            pi_t_list_q6[i] = pi_eq_func(par['alpha'], par['gamma'], par['h'], start['y_n'], phi_usa, start['pi_n'], start['s_n'], v_t_list_q6[i], par['b'], s_t_list_q6[i])

            y_tm1_list_q6[i]  = start['y_n']
            pi_tm1_list_q6[i] = start['pi_n']

# 9) Set the values in the following periods.
        else:
            v_t_list_q6[i] = par['delta'] * v_t_list_q6[i-1] + x_t_list_q6[i]
            s_t_list_q6[i] = par['omega'] * s_t_list_q6[i-1] + c_t_list_q6[i]
            y_t_list_q6[i] = y_eq_func(par['alpha'], par['gamma'], par['h'], phi_usa, y_t_list_q6[i-1], s_t_list_q6[i-1], pi_t_list_q6[i-1], s_t_list_q6[i], v_t_list_q6[i], par['b'])
            pi_t_list_q6[i] = pi_eq_func(par['alpha'], par['gamma'], par['h'], y_t_list_q6[i-1], phi_usa, pi_t_list_q6[i-1], s_t_list_q6[i-1], v_t_list_q6[i], par['b'], s_t_list_q6[i])

            y_tm1_list_q6[i] = y_t_list_q6[i-1]
            pi_tm1_list_q6[i] = pi_t_list_q6[i-1]

# 10) Define the variance, the correlation and the auto-correlation.
    variance_y_q6 = np.var(y_t_list_q6)

    variance_pi_q6 = np.var(pi_t_list_q6)

    corr_y_pi_q6_old = np.corrcoef(y_t_list_q6, pi_t_list_q6)
    corr_y_pi_q6 = corr_y_pi_q6_old[1,0]

    auto_corr_y_q6_old = np.corrcoef(y_t_list_q6, y_tm1_list_q6)
    auto_corr_y_q6 = auto_corr_y_q6_old[1,0]

    auto_corr_pi_q6_old = np.corrcoef(pi_t_list_q6, pi_tm1_list_q6)
    auto_corr_pi_q6 = auto_corr_pi_q6_old[1,0]

# 11) Withdraw the real values and square the expression.
    variance_y_q6_new = (variance_y_q6 - var_y_given)**2
    variance_pi_q6_new = (variance_pi_q6 - var_pi_given)**2
    corr_y_pi_q6_new = (corr_y_pi_q6 - corr_given)**2
    auto_corr_y_q6_new = (auto_corr_y_q6 - auto_y_given)**2
    auto_corr_pi_q6_new = (auto_corr_pi_q6 - auto_pi_given)**2

# 12) Calculate the sum to be minimized.
    sum_min = variance_y_q6_new + variance_pi_q6_new + corr_y_pi_q6_new + auto_corr_y_q6_new + auto_corr_pi_q6_new

    return sum_min
```

We want to minimize the sum of differences between the real values of $\phi$, $\sigma_x$ and $\sigma_c$ and the parameters in our model. Thus, we want to calibrate our model to the real world so to say. Again, we use the optimize function from the `scipy` package.

```python
# 1) Take initial guess of the three parameters.
initial_guess_q6 = [0, 1, 1]

# 2) Define the constraint. phi must be compromised between 0 and 1, and variances must be positive.
constraint_q6 = ((0,1), (0.001,100**100), (0.001,100**100))

# 3) Optimize where we condition on the constraint.
result_q6 = optimize.minimize(bs_usa, initial_guess_q6, method='L-BFGS-B', bounds=constraint_q6)

# 4) Store the estimated parameters.
phi_cal = result_q6.x[0]
sigma_x_cal = result_q6.x[1]
sigma_c_cal = result_q6.x[2]

# 5) Print the calibrated parameters.
print('The calibrated value of phi is given by              phi = ' + str(round(phi_cal, 3)))
print('The calibrated value of sigma_x is given by      sigma_x = ' + str(round(sigma_x_cal, 3)))
print('The calibrated value of sigma_c is given by      sigma_c = ' + str(round(sigma_c_cal, 3)))
```

Finally, we can compare the moments in our sample after the calibration with the true moments in the US economy. We define a function where we insert the calibrated parameters in order to estimate the moments in our sample.

```python
# 1) Define function.
def compare(phi_comp, sigma_x_comp, sigma_c_comp):
    """ The procedure is almost identical to the procedure in the function "bs_usa". In this function, one can plug in
    different values of phi, sigma_x and sigma_c. The moments (varians, correlation, and auto-correlation) are returned
    as a function of the choice of these three parameters.
    """

# 2) Define length of period.
    total_comp = 1000

    # 3) Define empty lists.
    t_list_comp = np.empty(total_comp)
    x_t_list_comp = np.empty(total_comp)
    c_t_list_comp = np.empty(total_comp)
    v_t_list_comp = np.empty(total_comp)
    s_t_list_comp = np.empty(total_comp)
    y_t_list_comp = np.empty(total_comp)
    pi_t_list_comp = np.empty(total_comp)
    y_tm1_list_comp = np.empty(total_comp)
    pi_tm1_list_comp = np.empty(total_comp)

    # 4) Define starting parameters.
    start = {}
    start['y_n'] = 0
    start['pi_n'] = 0
    start['v_n'] = 0
    start['s_n'] = 0

    # 5) Define the length of the time vector.
    for i in range (0, total_comp):
        t_list_comp[i] = i

    # 6) Set a seed number and draw a random number from the normal distribution with mean 0 and variance sigma.
        np.random.seed(117)
        c_t_list_comp_old = np.random.normal(loc=0, scale=sigma_c_comp, size=(total_comp, 1))
        c_t_list_comp = c_t_list_comp_old[:,0]
        x_t_list_comp_old = np.random.normal(loc=0, scale=sigma_x_comp, size=(total_comp, 1))
        x_t_list_comp = x_t_list_comp_old[:,0]

    # 7) Set the values in the first period.
        if t_list_comp[i] == 0:
            v_t_list_comp[i] = par['delta'] * start['v_n'] + x_t_list_comp[i]
            s_t_list_comp[i] = par['omega'] * start['s_n'] + c_t_list_comp[i]
            y_t_list_comp[i] = y_eq_func(par['alpha'], par['gamma'], par['h'], phi_comp, start['y_n'], start['s_n'], start['pi_n'], s_t_list_comp[i], v_t_list_comp[i], par['b'])
            pi_t_list_comp[i] = pi_eq_func(par['alpha'], par['gamma'], par['h'], start['y_n'], phi_comp, start['pi_n'], start['s_n'], v_t_list_comp[i], par['b'], s_t_list_comp[i])

            y_tm1_list_comp[i]  = start['y_n']
            pi_tm1_list_comp[i] = start['pi_n']

    # 8) Set the values in the following periods.
        else:
            v_t_list_comp[i] = par['delta'] * v_t_list_comp[i-1] + x_t_list_comp[i]
            s_t_list_comp[i] = par['omega'] * s_t_list_comp[i-1] + c_t_list_comp[i]
            y_t_list_comp[i] = y_eq_func(par['alpha'], par['gamma'], par['h'], phi_comp, y_t_list_comp[i-1], s_t_list_comp[i-1], pi_t_list_comp[i-1], s_t_list_comp[i], v_t_list_comp[i], par['b'])
            pi_t_list_comp[i] = pi_eq_func(par['alpha'], par['gamma'], par['h'], y_t_list_comp[i-1], phi_comp, pi_t_list_comp[i-1], s_t_list_comp[i-1], v_t_list_comp[i], par['b'], s_t_list_comp[i])

            y_tm1_list_comp[i] = y_t_list_comp[i-1]
            pi_tm1_list_comp[i] = pi_t_list_comp[i-1]

    variance_y_comp = np.var(y_t_list_comp)

    variance_pi_comp = np.var(pi_t_list_comp)

    corr_y_pi_comp_old = np.corrcoef(y_t_list_comp, pi_t_list_comp)
    corr_y_pi_comp = corr_y_pi_comp_old[1,0]

    auto_corr_y_comp_old = np.corrcoef(y_t_list_comp, y_tm1_list_comp)
    auto_corr_y_comp = auto_corr_y_comp_old[1,0]

    auto_corr_pi_comp_old = np.corrcoef(pi_t_list_comp, pi_tm1_list_comp)
    auto_corr_pi_comp = auto_corr_pi_comp_old[1,0]

    return variance_y_comp, variance_pi_comp, corr_y_pi_comp, auto_corr_y_comp, auto_corr_pi_comp
```

We are now able to compare the moments graphically. First, we combine the moments in a list, and afterwards we plot the graph.

We note that some of the moments are alike, while other moments from the calibrated model differ from the true moments.

```python
# 1) Combine the calibrated parameters in a list.
model_cal = compare(phi_cal , sigma_x_cal, sigma_c_cal)

# 2) Combine the true moments in a list.
given_list = [var_y_given, var_pi_given, corr_given, auto_y_given, auto_pi_given]

# 3) Plot the graph
plt.figure(figsize=(12,8))
x = np.arange(len(model_cal))
bar_width = 0.30
plt.bar(x, model_cal, width=bar_width, color='blue', zorder=2)
plt.bar(x + bar_width, given_list, width=bar_width, color='red', zorder=2)
plt.xticks(x+bar_width/2, ['var(y_t)', 'var(pi_t)', 'c(y_t, pi_t)', 'c(y_t, y_t-1)', 'c(pi_t, pi_t-1)'])
plt.ylabel('Value')
plt.legend(['Moments after calibration', 'True moments'])
plt.show()
```

# 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
alphas = np.exp(np.random.multivariate_normal(mu, Sigma, size=N))
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.

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.

**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.

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

**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?

## Question 3.1

The budget share for good $j$ is defined by the demand for good $j$ relative to the total income

$$\text{budget share}_j = \frac{p_j x_{i}^{\star j}(p_{1},p_{2},\boldsymbol{e}^{j})}{I^J} = \frac{p_j \beta^{j}_i\frac{I^j}{p_{i}}}{I^J} = \beta^{j}_i$$

The budget shares are therefore equal to $\beta^{j}_i$

```python
# 1) Define the budget shares
budget_share_1 = betas[:,0]
budget_share_2 = betas[:,1]
budget_share_3 = betas[:,2]

# 2) Plot the budget shares
plt.figure(figsize=(12,8))
plt.hist(budget_share_1, bins=100, color="r", alpha=0.6)
plt.hist(budget_share_2, bins=100, color="b", alpha=0.6)
plt.hist(budget_share_3, bins=100, color="g", alpha=0.6)

# 3) Add labels and ledends and show the graph
plt.ylabel('Budget share')
plt.xlabel('N')
plt.legend(['Budget share good 1','Budget share good 2','Budget share good 3'])
axes = plt.gca()
axes.get_yaxis().set_major_formatter(
matplotlib.ticker.FuncFormatter(lambda x, p: format(int(x), ',')))
plt.show()
```

The histogram above shows that most of the agents will demand the largest budget share for good 1 and demand the lowest budget share for good 3.

## Question 3.2

We will start out by defining a function which returns the excess demand.

```python
# 1) Define the excess demand
def excess_demand(p1, p2, e1=e1, e2=e2, e3=e3, betas=betas):

    # 1.1) Find the income
    I = p1 * e1 + p2 * e2 + 1 * e3

    # 1.2) Find the demand from the demand function
    x1 = betas[:,0] * I / p1
    x2 = betas[:,1] * I / p2

    # 1.3) Find the excess demand
    z1 = sum(x1) - sum(e1)
    z2 = sum(x2) - sum(e2)
    return z1, z2
```

Now we will plot the excess demand for each three goods in a 3D plot with the excess demand on the z-axis and $p_1$ and $p_2$ on the x- and y-axis, respectively.

```python
# 1) Make a range over the prices
p1 = np.arange(1, 10, 0.4)
p2 = np.arange(1, 5, 0.4)

# 2) Make a meshgrid over x and y
X, Y = np.meshgrid(p1, p2)

# 3) Make a 3d plot of the excess demand for good 1 as a function of p1 and p2.
# 3.1) Create a figure
fig = plt.figure()
ax1 = fig.add_subplot(1,1,1, projection='3d')
fig.set_size_inches(12, 8)

# 3.2) Find the excess demand for good 1 and find the negative excess demand
z1 = np.array([excess_demand(x,y)[0] for x,y in zip(np.ravel(X), np.ravel(Y))])
Z1 = z1.reshape(X.shape)

Z1neg = Z1.copy()
Z1neg[Z1 > 0] = np.nan

# 3.3) Plot the excess demand for good 1 as a function of p1 and p2
ax1.plot_surface(X, Y, Z1, color='b')
ax1.plot_surface(X, Y, Z1neg, color='r')
ax1.invert_xaxis()

# 3.4) Add labels
ax1.set_title('Figur 3.2.1: Excess demand good 1')
ax1.set_xlabel('$P_1$')
ax1.set_ylabel('$P_2$')
ax1.set_zlabel('Excess demand')

# 4) Make a 3d plot of the excess demand for good 2 as a function of p1 and p2.
# 4.1) Create a figure
fig = plt.figure()
ax2 = fig.add_subplot(1,1,1, projection='3d')
fig.set_size_inches(12, 8)

# 4.2) Find the excess demand for good 2 and find the negative excess demand
z2 = np.array([excess_demand(x,y)[1] for x,y in zip(np.ravel(X), np.ravel(Y))])
Z2 = z2.reshape(X.shape)

Z2neg = Z2.copy()
Z2neg[Z2 > 0] = np.nan

# 4.3) Plot the excess demand for good 3 as a function of p1 and p2
ax2.plot_surface(X, Y, Z2, color='b')
ax2.plot_surface(X, Y, Z2neg, color='r')
ax2.invert_xaxis()

# 4.4) Add labels
ax2.set_title('Figur 3.2.2: Excess demand good 2')
ax2.set_xlabel('$P_1$')
ax2.set_ylabel('$P_2$')
ax2.set_zlabel('Excess demand')

plt.show()

print(f'Note: {Fore.BLUE}Blue{Style.RESET_ALL} areas represent positive excess demand and {Fore.RED}red{Style.RESET_ALL} areas represent negative excess demand')
```

Graph 3.2.1 shows that the excess demand for good 1 depends positively on $p_2$ and negatively on $p_1$. Graph 3.2.2 shows that the excess demand for good 2 depends positively on $p_1$ and negatively on $p_2$.

## Question 3.3

Below we have defined a function which will find the Wasras equilibrium using the tâtonnement process.

```python
# 1) Define the tâtonnement function.
def tatonnement(p1, p2, e1=e1, e2=e2, e3=e3, betas=betas, eps=0.1, kappa=1, N=N, max_iter=500):

    # 2) Define arrays which will contain the time and all the guesses for the value of p1 and p2.
    global P1, P2, T, p1_star, p2_star
    P1 = []
    P2 = []
    T = []

    # 3) Set the timer to zero. The tâtonnement process will stop if the number of iterations excessed the max_iter value
    t=0

    # 4) Create the loop in which the tâtonnement process will run
    while t<max_iter:

        # 5) Calculate the excess demand
        z1 = excess_demand(p1, p2, e1, e2, e3, betas)[0]
        z2 = excess_demand(p1, p2, e1, e2, e3, betas)[1]

        # 6) Stop if the absolute value of excess demands are lower than the critical value eps
        if np.absolute(z1) < eps and np.absolute(z2) < eps:
            print(f'The Walras equilibrium is p1 = {p1:.3f} and p2 = {p2:.3f}. Stoped after {t:.0f} iterations. ')
            print(f'The excess demand for good 1 is {z1:.3f} and the excess demand for good 2 is {z2:.3f}')
            p1_star = p1
            p2_star = p2
            return

        # 7) Change the value of p1 and p2 if the the absolute value of excess demands are lower than the critical value eps
        else:
            p1_star = p1
            p2_star = p2

            P1.append(p1)
            P2.append(p2)
            T.append(t)

            p1 += kappa * z1 / N
            p2 += kappa * z2 / N

        # 8) Add 1 to the number of iterations
        t += 1

    # 9) Print the current value of p1 and p2 if the number of iterations are higher than the max_iter value
    print(f'The Walras equilibrium is p1 = {p1:.3f} and p2 = {p2:.3f}. Stoped after {t:.0f} iterations. ')
    print(f'The excess demand for good 1 is {z1:.3f} and the excess demand for good 2 is {z2:.3f}')
    return

```

```python
tatonnement(4, 2, e1=e1, e2=e2, e3=e3, betas=betas, eps=0.1, kappa=1, N=N, max_iter=800)
```

We will now plot the p1's and the p2's as a function of the number of iteration

```python
# 1) Plot the estiamte for p1 and p2 as a function of the number of iterations
plt.figure(figsize=(12,8))
plt.plot(T, P1, color='b')
plt.plot(T, P2, color='r')
plt.ylabel('Price')
plt.xlabel('Number of iterations')
plt.legend(['$P_1$','$P_2$'])
plt.show()
```

The graph above shows that the estimate of $p_1$ and $p_2$ are fairly stable after 300 observations. This indicates that the true values of $p_1$ and $p_2$ are very close to 6,5 and 2,6 respectively.

## Question 3.4

First, we define a function which returns the utility level given the prices, the endowment and the preferences.

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

    # 1) Find the income
    I = p1 * e1 + p2 * e2 + 1 * e3

    # 2) Find the demand from the demand function
    x1 = betas[:,0] * I / p1
    x2 = betas[:,1] * I / p2
    x3 = betas[:,2] * I / 1

    # 3) Find the utility
    u = (x1**betas[:,0] * x2**betas[:,1] * x3**betas[:,2])**gamma

    return u
```

We now use this function to plot the distribution of the utilities and calculate the mean and the variance.

```python
# 1) Find the utilities for all the agents
u = utility(p1_star, p2_star, e1, e2, e3, betas, gamma)

# 2) Plot the utilities in a histogram
plt.figure(figsize=(12,8))
plt.hist(u, bins=100, color='b')
plt.xlabel('Utility')
axes = plt.gca()
axes.get_yaxis().set_major_formatter(
matplotlib.ticker.FuncFormatter(lambda x, p: format(int(x), ',')))
plt.show()

# 3) Calculate the mean and variance of the utilities
mean = np.mean(u)
var = np.var(u)
print(f' The mean of the utilities is {mean:.3f} and variance is {var:.3f}')
```

The distribution of the utilities are quite right skewed.

## Question 3.5

If all the agents have the same endowment, and the total size of the endowments are unchanged, then all endowment must be equal to the mean of the current endowment. We will therefore calculate the mean of the current endowments and make three array with N number of oberservations equal to the mean.

```python
# 1) Calculate the mean of the current endowments.
e1_mean = np.mean(e1)
e2_mean = np.mean(e2)
e3_mean = np.mean(e3)

# 2) Make three arrays with N number of observations equal to the mean of the previous endowments.
e1_new = np.asarray([e1_mean] * N)
e2_new = np.asarray([e2_mean] * N)
e3_new = np.asarray([e3_mean] * N)

```

Next, we will chech if the prices have changed from the changed endowments using the tatonnement function defined in problem 3.3

```python
tatonnement(4, 2, e1_new, e2_new, e3_new, betas=betas, eps=0.1, kappa=1, N=N, max_iter=800)
```

We see that the prices have not changed much. We define a function that plots the distribution of the utilities as a function of $\gamma$ based on the utility function defined in problem 3.4. Finally, we apply and interactive slider to change the value of $\gamma$

```python
def graph(gamma_par):
    # 1) Calculate the utilities for the agents
    u1 = utility(p1_star, p2_star, e1_new, e2_new, e3_new, betas, gamma)
    u2 = utility(p1_star, p2_star, e1_new, e2_new, e3_new, betas, gamma_par)

    # 2) Plot the utilities for gamma=0.8 and for gamma=gamma_par
    plt.figure(figsize=(12,8))
    bins = np.linspace(0.98, 2, 100)
    plt.hist(u1, bins=bins, color='r', alpha=0.5)
    plt.hist(u2, bins=bins, color='b', alpha=0.5)
    plt.xlabel('Utility')
    plt.legend(['0.8', gamma_par],title="Value of $\gamma$")
    axes = plt.gca()
    axes.get_yaxis().set_major_formatter(
    matplotlib.ticker.FuncFormatter(lambda x, p: format(int(x), ',')))

    # 3) Calculate the mean and the variance for gamma=0.8 and for gamma=gamma_par
    mean1 = np.mean(u1)
    mean2 = np.mean(u2)
    var1 = np.var(u1)
    var2 = np.var(u2)

    # 4) print and plot
    print(f' For gamma = {gamma_par:.2f}, the mean is {mean2:.3f} with a variance of {var2:.3f}')
    print(f' For gamma = {gamma:.2f}, the mean is {mean1:.3f} with a variance of {var1:.3f}')
    plt.show()

# 5) Apply an interactive widget
widgets.interact(graph,
    gamma_par=(0.1,2,0.05));
```

First of all, we see that the average income is sligtly hihger and the variance is much lower for an even distribution of endowments compared to an uneven distribution in probelm 3.4. The graph above shows that as $\gamma$ increases the average utility increases as well. However, the variance will also increase if $\gamma$ is increased. This shows that a higher $\gamma$ implies that the agents gets a higher utility but the inequality between the agents will increase.