# ETS models

The ETS models are a family of time series models with an underlying state space model consisting of a level component, a trend component (T), a seasonal component (S), and an error term (E).

This notebook gives a very brief introduction to these models and shows how they can be used with statsmodels. For a more thorough treatment we refer to [1], chapter 8 (free online resource), on which the implementation in statsmodels and the examples used in this notebook are based.

[1] Hyndman, Rob J., and George Athanasopoulos. *Forecasting: principles and practice*, 3rd edition, OTexts, 2019. https://www.otexts.org/fpp3/7

```python
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
%matplotlib inline
from statsmodels.tsa.exponential_smoothing.ets import ETSModel
```

```python
plt.rcParams['figure.figsize'] = (12, 8)
```

## Simple exponential smoothing

The simplest of the ETS models is also known as *simple exponential smoothing*. In ETS terms, it corresponds to the (A, N, N) model, that is, a model with additive errors, no trend, and no seasonality. The state space formulation of Holt's method is:

\begin{align}
y_{t} &= y_{t-1} + e_t\\
l_{t} &= l_{t-1} + \alpha e_t\\
\end{align}

This state space formulation can be turned into a different formulation, a forecast and a smoothing equation (as can be done with all ETS models):

\begin{align}
\hat{y}_{t|t-1} &= l_{t-1}\\
l_{t} &= \alpha y_{t-1} + (1 - \alpha) l_{t-1}
\end{align}

Here, $\hat{y}_{t|t-1}$ is the forecast/expectation of $y_t$ given the information of the previous step. In the simple exponential smoothing model, the forecast corresponds to the previous level. The second equation (smoothing equation) calculates the next level as weighted average of the previous level and the previous observation.

```python
oildata = [
    111.0091, 130.8284, 141.2871, 154.2278,
    162.7409, 192.1665, 240.7997, 304.2174,
    384.0046, 429.6622, 359.3169, 437.2519,
    468.4008, 424.4353, 487.9794, 509.8284,
    506.3473, 340.1842, 240.2589, 219.0328,
    172.0747, 252.5901, 221.0711, 276.5188,
    271.1480, 342.6186, 428.3558, 442.3946,
    432.7851, 437.2497, 437.2092, 445.3641,
    453.1950, 454.4096, 422.3789, 456.0371,
    440.3866, 425.1944, 486.2052, 500.4291,
    521.2759, 508.9476, 488.8889, 509.8706,
    456.7229, 473.8166, 525.9509, 549.8338,
    542.3405
]
oil = pd.Series(oildata, index=pd.date_range('1965', '2013', freq='AS'))
oil.plot()
plt.ylabel("Annual oil production in Saudi Arabia (Mt)");
```

The plot above shows annual oil production in Saudia Arabia in million tonnes. The data are taken from the R package `fpp2` (companion package to prior version [1]).
Below you can see how to fit a simple exponential smoothing model using statsmodel's ETS implementation to this data. Additionally, the fit using `forecast` in R is shown as comparison.

```python
model = ETSModel(oil, error='add', trend='add', damped_trend=True)
fit = model.fit(maxiter=10000)
oil.plot(label='data')
fit.fittedvalues.plot(label='statsmodels fit')
plt.ylabel("Annual oil production in Saudi Arabia (Mt)");

# obtained from R
params_R = [0.99989969, 0.11888177503085334, 0.80000197, 36.46466837, 34.72584983]
yhat = model.smooth(params_R).fittedvalues
yhat.plot(label='R fit', linestyle='--')

plt.legend();
```

By default the initial states are considered to be fitting parameters and are estimated by maximizing log-likelihood. Additionally it is possible to only use a heuristic for the initial values. In this case this leads to better agreement with the R implementation.

```python
model_heuristic = ETSModel(oil, error='add', trend='add', damped_trend=True,
                          initialization_method='heuristic')
fit_heuristic = model_heuristic.fit()
oil.plot(label='data')
fit.fittedvalues.plot(label='estimated')
fit_heuristic.fittedvalues.plot(label='heuristic', linestyle='--')
plt.ylabel("Annual oil production in Saudi Arabia (Mt)");

# obtained from R
params = [0.99989969, 0.11888177503085334, 0.80000197, 36.46466837, 34.72584983]
yhat = model.smooth(params).fittedvalues
yhat.plot(label='with R params', linestyle=':')

plt.legend();
```

```python
fit.summary()
```

```python
fit_heuristic.summary()
```

## Holt-Winters' seasonal method

The exponential smoothing method can be modified to incorporate a trend and a seasonal component. In the additive Holt-Winters' method, the seasonal component is added to the rest. This model corresponds to the ETS(A, A, A) model, and has the following state space formulation:

\begin{align}
y_t &= l_{t-1} + b_{t-1} + s_{t-m} + e_t\\
l_{t} &= l_{t-1} + b_{t-1} + \alpha e_t\\
b_{t} &= b_{t-1} + \beta e_t\\
s_{t} &= s_{t-m} + \gamma e_t
\end{align}

```python
austourists_data = [
    30.05251300, 19.14849600, 25.31769200, 27.59143700,
    32.07645600, 23.48796100, 28.47594000, 35.12375300,
    36.83848500, 25.00701700, 30.72223000, 28.69375900,
    36.64098600, 23.82460900, 29.31168300, 31.77030900,
    35.17787700, 19.77524400, 29.60175000, 34.53884200,
    41.27359900, 26.65586200, 28.27985900, 35.19115300,
    42.20566386, 24.64917133, 32.66733514, 37.25735401,
    45.24246027, 29.35048127, 36.34420728, 41.78208136,
    49.27659843, 31.27540139, 37.85062549, 38.83704413,
    51.23690034, 31.83855162, 41.32342126, 42.79900337,
    55.70835836, 33.40714492, 42.31663797, 45.15712257,
    59.57607996, 34.83733016, 44.84168072, 46.97124960,
    60.01903094, 38.37117851, 46.97586413, 50.73379646,
    61.64687319, 39.29956937, 52.67120908, 54.33231689,
    66.83435838, 40.87118847, 51.82853579, 57.49190993,
    65.25146985, 43.06120822, 54.76075713, 59.83447494,
    73.25702747, 47.69662373, 61.09776802, 66.05576122,
]
index = pd.date_range("1999-03-01", "2015-12-01", freq="3MS")
austourists = pd.Series(austourists_data, index=index)
austourists.plot()
plt.ylabel('Australian Tourists');
```

```python
# fit in statsmodels
model = ETSModel(austourists, error="add", trend="add", seasonal="add",
                damped_trend=True, seasonal_periods=4)
fit = model.fit()

# fit with R params
params_R = [
    0.35445427, 0.03200749, 0.39993387, 0.97999997, 24.01278357,
    0.97770147, 1.76951063, -0.50735902, -6.61171798, 5.34956637
]
fit_R = model.smooth(params_R)

austourists.plot(label='data')
plt.ylabel('Australian Tourists')

fit.fittedvalues.plot(label='statsmodels fit')
fit_R.fittedvalues.plot(label='R fit', linestyle='--')
plt.legend();
```

```python
fit.summary()
```

```python
fit._rank
```

```python

```


$$ \LaTeX \text{ command declarations here.}
\newcommand{\R}{\mathbb{R}}
\renewcommand{\vec}[1]{\mathbf{#1}}
\newcommand{\X}{\mathcal{X}}
\newcommand{\D}{\mathcal{D}}
\newcommand{\G}{\mathcal{G}}
\newcommand{\Parents}{\mathrm{Parents}}
\newcommand{\NonDesc}{\mathrm{NonDesc}}
\newcommand{\I}{\mathcal{I}}
$$

## Exponential Family Distributions

**DEF:** $p(x | \theta)$ has **exponential family form** if:
$$
\begin{align}
p(x | \theta)
&= \frac{1}{Z(\theta)} h(x) \exp\left[ \eta(\theta)^T \phi(x) \right] \\
&= h(x) \exp\left[ \eta(\theta)^T \phi(x) - A(\theta) \right]
\end{align}
$$

- $Z(\theta)$ is the **partition function** for normalization
- $A(\theta) = \log Z(\theta)$ is the **log partition function**
- $\phi(x) \in \R^d$ is a vector of **sufficient statistics**
- $\eta(\theta)$ maps $\theta$ to a set of **natural parameters**
- $h(x)$ is a scaling constant, usually $h(x)=1$

### Problem:  Bernoulli in exponential family form

As mentioned in lecture, the *Bernoulli* distribution can be described in expontial family form:
$$
\begin{align}
\mathrm{Ber}(x | \mu)
&= \mu^x (1-\mu)^{1-x} \\
&= (1-\mu) \exp\left[ x \log\frac{\mu}{1-\mu} \right]
\end{align}
$$

This gives **natural parameters** $\eta = \log \frac{\mu}{1-\mu}$.

**Problem**: What is the log partition function $A(\eta)$?

## Problem: Guassian Distribution in exp family form

Recall that the gaussian distribution can be written as
$$p(x \mid \mu, \sigma^2) = \frac{1}{\sqrt{2 \pi \sigma^2}} \exp\left(-\frac{(\mu-x)^2}{2 \sigma^2} \right)$$

**Problem**: Write this in exponential family form. Questions:
1. What is $\phi(x)$?
1. What is the parameterization $\eta(\mu,\sigma^2)$?
1. What is the log-partition $A(\eta)$?

## Magic of the log partition function

Derivatives of the **log-partition function** $A(\theta)$ yield **cumulants** of the sufficient statistics (assume $h(x)=1$ and $\eta(\theta) = \theta$):
- $\nabla_\theta A(\theta) = E_{x}[\phi(x)]$
- $\nabla^2_\theta A(\theta) = \text{Cov}[ \phi(x) ]$

**Problem**: Prove the first statement using calculus.

*Hint*: While this fact isn't always true, in this case you can use the following:
$$\nabla_\theta \left( \int f(x,\theta) dx \right) =  \int \nabla_\theta f(x,\theta) dx$$

## Problem: Conditional independence in PGMs

**Which of the following are true?**:
1. $P(C,B|A) = P(C|A) P(B |A)$
2. $P(C,B) = P(C) P(B)$

Prove your answer!

## Problem: Conditional independence in PGMs

**Which of the following are true?**:
1. $P(C,B|A) = P(C|B,A)$
2. $P(C,B) = P(C) P(B)$

Prove your answer!

## Bayesian Networks

Let's consider a bayesian network representing different factors that determine whether you make it to class on time:

### Exercise: express joint distribution in terms of product of factors

Express the joint distribution of this network using the chain rule.

P(W,R,T,S,B,C) = ...

*your answer goes here*

```python
%%html
<style>
div.probs table {
    float:left;
    margin-left: 1em;
    margin-top: 1em;
}

div.probs br.clear {
    clear:both;
}
</style>

```

### Computing Probabilities

Assuming the (conditional) distributions are as follows:

$w^0$
$w^1$

0.2
0.8

$r^0$
$r^1$

0.68
0.32

$t^0$
$t^1$

0.85
0.15

$s^0$
$s^1$

$w^0$, $r^0$	0.4	0.6

$w^0$, $r^1$	0.9	0.1

$w^1$, $r^0$	0.05	0.95

$w^1$, $r^1$	0.3	0.7

$b^0$	$b^1$

$t^0$	0.15	0.85

$t^1$	0.70.3

$c^0$
$c^1$

$s^0$, $b^0$	0.4	0.6

$s^0$, $b^1$	0.9	0.1

$s^1$, $b^0$	0.05	0.95

$s^1$, $b^1$	0.1	0.9

What is the probability you wake up on time, your roomate takes a long shower, there's bad traffic, you and the bus are both late to the bus stop and you make it to class on time?

*your answer goes here*

### Exercise: Marginalizatioin

What is the probability that you get to the bus stop on time given you wake up on time?

*your answer goes here*

### Exercise: v-structure

What is the probability that you get to the bus stop on time given that you make it to class on time? $P(S|C)$

What is the probability that you get to the bus stop on time given that you make it to class on time and bus arrives on time? $P(S|C,B)$

```python

```


### Calculations used for Tsmp

```
from sympy import *
init_printing()
```

#### Post synaptic dendritic current

Tfd (Tf difference) below represents the difference between $t$ (current time) and $Tf[i]$ (last pre synaptic spike time for dendrite i).  E. g. result of $t - Tf[i]$.

```
Tfd = var('Tfd')
```

Dt = Delta time
A dirac delta formula from 'Theoretical Neuroscience' page 405: $DiracDelta = lim(Dt->0)(if (-Dt/2) < t < Dt)$: return $1/Dt$; else return $0$;).  Due to that the formula $1/t$ for DiracDelta is able to be used when the if statement condition is matched.  It is represented as $1/Tfd$ in this case.

Formula 1 in the Gupta paper is   $T_d[i]*d(I_d[i][t])=-I_d[i][t]+R_d[i]*w[i]*(\frac{1}{Tfd})$

$T_d[i]$ can be brought to the other side of the equation through multipling $1/T_d[i]$ on both sides.  This produces $d(I_d[i][t])=(1/T_d[i])*-I_d[i][t]+R_d[i]*w[i]*(1/Tfd)$

The fundamental theorum of calculus is that $integral(derivative(f(x)))=f(x)$.  A.k.a. an integral can cancel a derivative out of an equation.  To get $Id[i]$ from formula 1 the integral of both sides should be taken to remove the derivative from the formula.

```
Id, Td, Rd, W = var('I_d T_d R_d W')
```

```
Id2 = Id
```

```
Id = ((1/Td)*-Id2+Rd*W*(1/Tfd))
```

```
Id.integrate(Id2)
```

#### Post synaptic direct synapse soma current

15 input neurons.  $W = weight, 1/Tdf = Dirac function$.  Using the formula $Sigma(w*Dirac)$ the output below is produced.

```
SummedWeights1 = var('SummedWeights1')
```

```
DiracWeightedSum = SummedWeights1/Tfd
```

```
Is_1, Ts_1 = var('Is_1 Ts_1')
```

```
Is_1b = Is_1
```

```
Is_1 = (1/Ts_1)*(-Is_1b+DiracWeightedSum)
```

###### Total soma membrane potential

```
DendriteGroup = [Id.integrate(Id2)]*15
```

```
SynapseToSoma = Is_1.integrate(Is_1b)
```

#### Total soma membrane potential calculation

It was found that end result variables representing prior component
calculations (SummedDendriteGroupX, SynapseToSomaX) could be applied
to solving for um in the equation.

```
Um, Tm, Rm = var('U_m T_m R_m')
```

```
Uu2 = (1/Tm)*(-Um+(Rm*(sum(DendriteGroup)+SynapseToSoma)))
```

```
Uu2.integrate(Um)
```

Below are shown the end result variables which can simplify the formula
used instead of the above one

```
DiracWeightedSum_Z, Is_Z = var('DiracWeightedSum_Z Is_Z')
```

```
Is_Zb = Is_Z
```

```
Is_Z = (1/Ts_1)*(-Is_Zb+DiracWeightedSum_Z)
```

```
SynapseToSoma_Z = Is_Z.integrate(Is_Zb)
```

```
SynapseToSoma_Z
```

```
SummedDendriteGroupX, SynapseToSomaX = var('SummedDendriteGroupX SynapseToSomaX')
```

```
Uu3 = (1/Tm)*(-Um+(Rm*(SummedDendriteGroupX+SynapseToSomaX)))
```

The integration below solves the tsmp equation for Um and therefore
provides the tsmp result wanted.

```
Uu3.integrate(Um)
```


In this notebook, you will implement the forward longitudinal vehicle model. The model accepts throttle inputs and steps through the longitudinal dynamic equations. Once implemented, you will be given a set of inputs that drives over a small road slope to test your model.

The input to the model is a throttle percentage $x_\theta \in [0,1]$ which provides torque to the engine and subsequently accelerates the vehicle for forward motion.

The dynamic equations consist of many stages to convert throttle inputs to wheel speed (engine -> torque converter -> transmission -> wheel). These stages are bundled together in a single inertia term $J_e$ which is used in the following combined engine dynamic equations.

\begin{align}
    J_e \dot{\omega}_e &= T_e - (GR)(r_{eff} F_{load}) \\ m\ddot{x} &= F_x - F_{load}
\end{align}

Where $T_e$ is the engine torque, $GR$ is the gear ratio, $r_{eff}$ is the effective radius, $m$ is the vehicle mass, $x$ is the vehicle position, $F_x$ is the tire force, and $F_{load}$ is the total load force.

The engine torque is computed from the throttle input and the engine angular velocity $\omega_e$ using a simplified quadratic model.

\begin{align}
    T_e = x_{\theta}(a_0 + a_1 \omega_e + a_2 \omega_e^2)
\end{align}

The load forces consist of aerodynamic drag $F_{aero}$, rolling friction $R_x$, and gravitational force $F_g$ from an incline at angle $\alpha$. The aerodynamic drag is a quadratic model and the friction is a linear model.

\begin{align}
    F_{load} &= F_{aero} + R_x + F_g \\
    F_{aero} &= \frac{1}{2} C_a \rho A \dot{x}^2 = c_a \dot{x}^2\\
    R_x &= N(\hat{c}_{r,0} + \hat{c}_{r,1}|\dot{x}| + \hat{c}_{r,2}\dot{x}^2) \approx c_{r,1} \dot{x}\\
    F_g &= mg\sin{\alpha}
\end{align}

Note that the absolute value is ignored for friction since the model is used for only forward motion ($\dot{x} \ge 0$).

The tire force is computed using the engine speed and wheel slip equations.

\begin{align}
    \omega_w &= (GR)\omega_e \\
    s &= \frac{\omega_w r_e - \dot{x}}{\dot{x}}\\
    F_x &= \left\{\begin{array}{lr}
        cs, &  |s| < 1\\
        F_{max}, & \text{otherwise}
        \end{array}\right\}
\end{align}

Where $\omega_w$ is the wheel angular velocity and $s$ is the slip ratio.

We setup the longitudinal model inside a Python class below. The vehicle begins with an initial velocity of 5 m/s and engine speed of 100 rad/s. All the relevant parameters are defined and like the bicycle model, a sampling time of 10ms is used for numerical integration.

```python
import sys
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.image as mpimg

class Vehicle():
    def __init__(self):

        # ==================================
        #  Parameters
        # ==================================

        #Throttle to engine torque
        self.a_0 = 400
        self.a_1 = 0.1
        self.a_2 = -0.0002

        # Gear ratio, effective radius, mass + inertia
        self.GR = 0.35
        self.r_e = 0.3
        self.J_e = 10
        self.m = 2000
        self.g = 9.81

        # Aerodynamic and friction coefficients
        self.c_a = 1.36
        self.c_r1 = 0.01

        # Tire force
        self.c = 10000
        self.F_max = 10000

        # State variables
        self.x = 0
        self.v = 5
        self.a = 0
        self.w_e = 100
        self.w_e_dot = 0

        self.sample_time = 0.01

    def reset(self):
        # reset state variables
        self.x = 0
        self.v = 5
        self.a = 0
        self.w_e = 100
        self.w_e_dot = 0
```

Implement the combined engine dynamic equations along with the force equations in the cell below. The function $\textit{step}$ takes the throttle $x_\theta$ and incline angle $\alpha$ as inputs and performs numerical integration over one timestep to update the state variables. Hint: Integrate to find the current position, velocity, and engine speed first, then propagate those values into the set of equations.

```python
class Vehicle(Vehicle):
    def step(self, throttle, alpha):
        # ==================================
        #  Implement vehicle model here
        # ==================================
        #tire force (F_x) calculation using engine speed / wheel angular velocity (w_w) and the slip ratio(s)
        w_w = self.GR * self.w_e
        s = (w_w * self.r_e - self.v) / self.v
        self.s = s
        if abs(s) < 1:
            F_x = self.c * s
        else:
            F_x = self.F_max

        # Load forces (F_load)= aerodynamic drag(F_aero)+rolling friction(R_x)+ gravitational force(F_g)
        F_aero = self.c_a * (self.v ** 2)
        R_x = self.c_r1 * self.v
        F_g = self.m * self.g * np.sin(alpha)
        F_load = F_aero + R_x + F_g

        # Calculate state variables: x,v,a,w_e and w_e_dot
        self.a = (F_x - F_load) / self.m
        self.v += self.a * self.sample_time
        self.x += (2 * self.v - self.a * self.sample_time) / 2 * self.sample_time

        # T_e
        T_e = throttle * (self.a_0 + self.a_1 * self.w_e + self.a_2 * self.w_e ** 2)

        # w_e_dot
        r_eff = self.r_e
        self.w_e_dot = (T_e - self.GR * r_eff * F_load) / self.J_e

        # w_e
        self.w_e += self.w_e_dot * self.sample_time
```

Using the model, you can send constant throttle inputs to the vehicle in the cell below. You will observe that the velocity converges to a fixed value based on the throttle input due to the aerodynamic drag and tire force limit. A similar velocity profile can be seen by setting a negative incline angle $\alpha$. In this case, gravity accelerates the vehicle to a terminal velocity where it is balanced by the drag force.

```python
sample_time = 0.01
time_end = 100
model = Vehicle()
model.reset()

t_data = np.arange(0,time_end,sample_time)
v_data = np.zeros_like(t_data)

# throttle percentage between 0 and 1
throttle = 0.2

# incline angle (in radians)
alpha = 0

for i in range(t_data.shape[0]):
    v_data[i] = model.v
    model.step(throttle, alpha)

plt.plot(t_data, v_data)
plt.show()
```

We will now drive the vehicle over a slope as shown in the diagram below.

To climb the slope, a trapezoidal throttle input is provided for the next 20 seconds as shown in the figure below.

The vehicle begins at 20% throttle and gradually increases to 50% throttle. This is maintained for 10 seconds as the vehicle climbs the steeper slope. Afterwards, the vehicle reduces the throttle to 0.

In the cell below, implement the ramp angle profile $\alpha (x)$ and throttle profile $x_\theta (t)$ and step them through the vehicle dynamics. The vehicle position $x(t)$ is saved in the array $\textit{x_data}$. This will be used to grade your solution.

```python
time_end = 20
t_data = np.arange(0,time_end,sample_time)
x_data = np.zeros_like(t_data)
v_data = np.zeros_like(t_data)
s_data = np.zeros_like(t_data)
throttle_data = np.zeros_like(t_data)
alpha_data = np.ones_like(t_data) * np.arctan(3 / 60)

# reset the states
model.reset()

# ==================================
#  Learner solution begins here
# ==================================
for i, t in enumerate(t_data):
    # throttle percentage between 0 and 1
    if t < 5:
        throttle_data[i] = 0.2 + (0.5 - 0.2) / 5 * t
    elif t < 15:
        throttle_data[i] = 0.5
    else:
        throttle_data[i] = 0.5 - 0.5 / 5 * (t - 15)

for i in range(t_data.shape[0]):
    v_data[i] = model.v
    if model.x < 60:
        alpha_data[i] = np.arctan(3 / 60)
    elif model.x < 150:
        alpha_data[i] = np.arctan((12-3) / 90)
    else:
        alpha_data[i] = 0

    model.step(throttle_data[i], alpha_data[i])
    x_data[i] = model.x
    #v_data[i] = model.v
    #s_data[i] = model.s

# ==================================
#  Learner solution ends here
# ==================================

# Plot x vs t for visualization
plt.figure(figsize = [8, 5])
plt.plot(t_data, throttle_data)
plt.title("Throttle vs Time")

plt.show()
```

If you have implemented the vehicle model and inputs correctly, you should see that the vehicle crosses the ramp at ~15s where the throttle input begins to decrease.

The cell below will save the time and vehicle inputs as text file named $\textit{xdata.txt}$. To locate the file, change the end of your web directory to $\textit{/notebooks/Course_1_Module_4/xdata.txt}$

Once you are there, you can download the file and submit to the Coursera grader to complete this assessment.

```python
data = np.vstack([t_data, x_data]).T
np.savetxt('xdata.txt', data, delimiter=', ')
```

Congratulations! You have now completed the assessment! Feel free to test the vehicle model with different inputs in the cell below, and see what trajectories they form. In the next module, you will see the longitudinal model being used for speed control. See you there!

```python
sample_time = 0.01
time_end = 30
model.reset()

t_data = np.arange(0,time_end,sample_time)
y_data = np.zeros_like(t_data)

# ==================================
#  Test various inputs here
# ==================================
for i in range(t_data.shape[0]):

    model.step(0,0)

plt.axis('equal')
plt.plot(x_data, y_data)
plt.show()
```


```python
import sympy
import numpy as np
import pandas as pd
import scipy as sp
import sklearn as sl
import numpy as np
import seaborn as sns; sns.set()
import matplotlib as mpl
from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d import axes3d
from matplotlib import cm
%matplotlib inline
from scipy.integrate import quad
from scipy import integrate
from sympy import fft
from scipy.fftpack import fft
```

# Tarea 4

Con base a los métodos vistos en clase resuelva las siguientes dos preguntas

## (A) Integrales

* $\int_{0}^{1}x^{-1/2}\,\text{d}x$
* $\int_{0}^{\infty}e^{-x}\ln{x}\,\text{d}x$
* $\int_{0}^{\infty}\frac{\sin{x}}{x}\,\text{d}x$

```python
def f(x):
    return x**(-0.5)

n=1000000

def Integrando1(f):
    x,y = np.linspace(0,1, num = n +1, retstep = True)
    return (5/4)*y*f(x[0] + f(x[-1])) + y*np.sum(f(x[1:-1]))

Integrando1(f)

```

    1.9985403954911485

```python
def f(x):
    return math.exp(-x)

def trapecio2(f, n, a, b):
    h = (b - a) / float(n)
    integrando = 0.5 * h * (f(a) + f(b))
    for i in range(1, int(n)):
        integrando = integrando + h * f(a + i * h)
    return integrando

a = 0
b = 10
n = 100

while(abs(trapecio2(f, n, a, b) - trapecio2(f, n * 4, a * 2, b * 2)) > 1e-6):
    n *= 4
    a *= 2
    b *= 2

trapecio2(f, n, a, b)

```

    1.0000008138019307

```python
def Integrando2(x):
   funcion = np.exp(-x)
   return funcion
solucion2 = quad(Integrando2,0,np.inf)

solucion2
```

    (1.0000000000000002, 5.842606996763696e-11)

```python
Integrando3 = integrate.quad(lambda x : (np.sin(x))/x, 0, np.inf)[0]
print("valor exacto de la integral 3:", Integrando3)
```

    valor exacto de la integral 3: 2.247867963468919

    <ipython-input-101-0b70c1f926b8>:1: IntegrationWarning: The integral is probably divergent, or slowly convergent.
      Integrando3 = integrate.quad(lambda x : (np.sin(x))/x, 0, np.inf)[0]

```python
n=100
x = np.linspace(0.000001,n,1000001)

f = []
for i in range(len(x)):
    f.append(np.sin(x[i])/x[i])

f = np.array(f)

def integrate(in_x,ft_f)->float:

    calculo=0
    for i in range(len(x)-1):
        calculo = calculo + ((ft_f[i+1])+(ft_f[i]))*abs(in_x[i+1]-in_x[i])/2

    return(calculo)

integral_3 = integrate(x,f)

print(f" Integrando 3  {integral_3}")

```

     Integrando 3  1.5622244668962069

## (B) Fourier

Calcule la transformada rápida de Fourier para la función de la **Tarea 3 (D)** en el intervalo $[0,4]$ ($k$ máximo $2\pi n/L$ para $n=25$). Ajuste la transformada de Fourier para los datos de la **Tarea 3** usando el método de regresión exacto de la **Tarea 3 (C)** y compare con el anterior resultado. Para ambos ejercicios haga una interpolación y grafique para comparar.

```python
df = pd.read_pickle(r"C:\Users\Camilo Rueda\Downloads\ex1.gz")
sns.scatterplot(x='x',y='y',data=df)
plt.show()
df
```

```python
x = df["x"]
y = df["y"]

lx = []
ly = []
for i in range(len(x)):
    if x[i]<=1.5 :
        lx.append(x[i])
        ly.append(y[i])

x = np.array(lx)
y = np.array(ly)

def f(p,x):
    return (p[0])/((x-p[1])**2 + p[2])**p[3]

def L_ajuste(p,x,y):
    deltaY=f(p,x) - y
    return np.dot(deltaY,deltaY)/len(y)

Nf = 25

def a_j(j):
    global x, y
    k_j = 2*np.pi*j/4
    n_y = y*np.cos(k_j*x)
    return integrate.simpson(n_y, x)

def b_j(j):
    global x, y
    k_j = 2*np.pi*j/4
    n_y = y*np.sin(k_j*x)
    return integrate.simpson(n_y, x)

A_j = np.array([a_j(j) for j in range(Nf)])
B_j = np.array([b_j(j) for j in range(Nf)])

```

```python
x_tilde = np.linspace(0, 4, 10000)
k_j = np. array([2*np.pi*j/4 for j in range(Nf)])
y_tilde = np.sum([  (A_j[j]*np.cos(k_j[j]*x_tilde)  +  B_j[j]*np.sin(k_j[j]*x_tilde)) for j in range(Nf)  ], axis=0)

```

```python
plt.plot(x,y)
plt.plot(x_tilde, y_tilde)
```


```python
import numpy as np
from scipy import linalg
from scipy import optimize
from scipy import interpolate

import sympy as sm
%matplotlib inline
import matplotlib.pyplot as plt
%matplotlib inline
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).

**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: Solve the model in period 2 and illustrate the solution (including labor supply as a function of human capital).

In line with problemset 7 and lecture 10:
    1. basic functions
    2. solution functions
    3. graphs

```python
def c(w,h,l,b):
    return w*h*l + b*(1-l)

def utility(w,h,l,b,rho):
    return c(w,h,l,b)**(1-rho)/(1-rho)

def disutility(gamma,l):
    return gamma*l

def v2(w,h2,l2,b,rho,gamma):
    return utility(w,h2,l2,b,rho)-disutility(gamma,l2)
```

Parameters

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

```python
def solve_period_2(w,b,rho,gamma,Delta):

    # a. grids
    h2_vec = np.linspace(0.1,1.5,100)
    v2_vec = np.empty(100)
    l2_vec = np.empty(100)

    # b. solve for each h2 in grid
    for i,h2 in enumerate(h2_vec):

        obj = lambda l2: -1* v2(w,h2,l2,b,rho,gamma)

        # ii. initial value
        x0 = h2/2

        # iii. optimizer
        result = optimize.minimize_scalar(obj,x0,method='bounded',bounds=[0,1])

        # iv. save
        v2_vec[i] = -result.fun
        l2_vec[i] = result.x

    return h2_vec,v2_vec,l2_vec

```

```python
# solve
h2_vec,v2_vec,l2_vec = solve_period_2(w,b,rho,gamma,Delta)
# illustration
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
ax.plot(h2_vec,l2_vec)
ax.grid()
ax.set_xlabel('human capital $h_2$')
ax.set_ylabel('labour supply $l_2$')
ax.set_title('human capital in period 2')

```

The worker is willing to work at a certain level of human capital. This level is slightly above 0.5. Below she will not work.

```python
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
ax.plot(h2_vec,v2_vec)
ax.grid()
ax.set_xlabel('human capital $h_2$')
ax.set_ylabel('utility $v_2$')
ax.set_title('value of living in period 2')

```

We see that her net utility is decreasing when she gets to a certain level of human capital (approximately 0.5)

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

In line with problemset 7 and lecture 10:
    1. basic functions
    2. solution functions
    3. graphs

```python
def c(w,h,l,b):
    return w*h*l + b*(1-l)

def utility(w,h,l,b,rho):
    return c(w,h,l,b)**(1-rho)/(1-rho)

def disutility(gamma,l):
    return gamma*l

def v2(w,h2,l2,b,rho,gamma):
    return utility(w,h2,l2,b,rho)-disutility(gamma,l2)
```

```python
v2_interp = interpolate.RegularGridInterpolator([h2_vec], v2_vec,
                                                bounds_error=False,fill_value=None)
```

```python
def v1(w,h1,l1,b,rho,gamma,Delta,beta,v2_interp):

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

    # a. v2 value, if high h2
    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. total value
    return utility(w,h1,l1,b,rho)-disutility(gamma,l1) + beta*v2
```

Parameters

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

```python
def solve_period_1(w,b,rho,beta,Delta,gamma,v2_interp):

    # a. grids
    h1_vec = np.linspace(0.1,1.5,100)
    v1_vec = np.empty(100)
    l1_vec = np.empty(100)

    # b. solve for each m1 in grid
    for i,h1 in enumerate(h1_vec):

        obj = lambda l1: -1*v1(w,h1,l1,b,rho,gamma,Delta,beta,v2_interp)

        # ii. initial guess (consume half)
        x0 = h1/2

        # iii. optimize
        result = optimize.minimize_scalar(obj,x0,method='bounded',bounds=[0,1])

        # iv. save
        v1_vec[i] = -result.fun
        l1_vec[i] = result.x

    return h1_vec,v1_vec,l1_vec
```

```python
# solve
h1_vec,v1_vec,l1_vec = solve_period_1(w,b,rho,beta,Delta,gamma,v2_interp)

# illustrat
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
ax.plot(h1_vec,l1_vec)
ax.grid()
ax.set_xlabel('human capital $h_1$')
ax.set_ylabel('labour supply $l_1$')
ax.set_title('human capital in period 1')

```

In period 1 the worker is willing to work at a level of human capital which is above 0.2. At a certain piont of human capital the labour supply increases slower.

There might be an mistake in the code. But we did it in line to question 1.

```python
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
ax.plot(h1_vec,v1_vec)
ax.grid()
ax.set_xlabel('human capital $h_1$')
ax.set_ylabel('utility $v_1$')
ax.set_title('value of living in period 1')

```

We see that her net utility is increases when she gets to a certain level of human capital (approximately 0.3). But before she reaches that piont her utility decreases at level of approximately 0.25 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.

We plot human capital and the value of living in both period, for different wages
1. w = 2
2. w = 0.9
3. w = 0.8
4. w = 0.5

**Period 2: Human Capital**

```python
h2_vec,v2_vec,l2_vec = solve_period_2(w,b,rho,gamma,Delta)

h2_vec1,v2_vec1,l2_vec1 = solve_period_2(0.9,b,rho,gamma,Delta)
h2_vec2,v2_vec2,l2_vec2 = solve_period_2(0.8,b,rho,gamma,Delta)
h2_vec3,v2_vec3,l2_vec3 = solve_period_2(0.5,b,rho,gamma,Delta)

# illustration
fig = plt.figure()
plt.xlim(0,2)
ax = fig.add_subplot(1,1,1)
ax.plot(h2_vec,l2_vec, label="w= 2")
ax.plot(h2_vec1,l2_vec1, label="w= 0.9")
ax.plot(h2_vec2,l2_vec2, label="w= 0.8")
ax.plot(h2_vec3,l2_vec3, label="w= 0.5")
ax.grid()
ax.legend()
ax.set_xlabel('human capital $h_2$')
ax.set_ylabel('labour supply $l_2$')
ax.set_title('human capital in period 2')
```

The unemployment benefits(b) are equal to 1. We see in this graph that the worker is also willing to work, if the wage (w) is smaller than b. But if the wage decreases, the level of human capital must increase to compensate the wage in her utility function.

```python
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
ax.plot(h2_vec,v2_vec, label="w= 2")
ax.plot(h2_vec1,v2_vec1, label="w= 0.9")
ax.plot(h2_vec2,v2_vec2, label="w= 0.8")
ax.plot(h2_vec3,v2_vec3, label="w= 0.5")
ax.grid()
ax.legend()
ax.set_xlabel('human capital $h_2$')
ax.set_ylabel('utility $v_2$')
ax.set_title('value of living in period 2')
```

The utility of working still increases, but on higher levels of human capital. But we also see that her utility is negative all the time, maybe she should take some hobbies into account.

**Period 1**

```python
# solve
h1_vec,v1_vec,l1_vec = solve_period_1(w,b,rho,beta,Delta,gamma,v2_interp)

h1_vec1,v1_vec1,l1_vec1 = solve_period_1(0.9,b,rho,beta,Delta,gamma,v2_interp)
h1_vec2,v1_vec2,l1_vec2 = solve_period_1(0.8,b,rho,beta,Delta,gamma,v2_interp)
h1_vec3,v1_vec3,l1_vec3 = solve_period_1(0.5,b,rho,beta,Delta,gamma,v2_interp)

# illustrat
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
ax.plot(h1_vec,l1_vec, label="w= 2")
ax.plot(h1_vec1,l1_vec1, label="w= 0.9")
ax.plot(h1_vec2,l1_vec2, label="w= 0.8")
ax.plot(h1_vec3,l1_vec3, label="w= 0.5")
ax.grid()
ax.legend()
ax.set_xlabel('human capital $h_1$')
ax.set_ylabel('labour supply $l_1$')
ax.set_title('human capital in period 1')
```

She will also work in period 1 if her wage is smaller than her unemployment benefits. But not as the example shows for w = 0.5.

We again take into account that there might be a mistake in the code.

```python
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
ax.plot(h1_vec,v1_vec, label="w= 2")
ax.plot(h1_vec1,v1_vec1, label="w= 0.9")
ax.plot(h1_vec2,v1_vec2, label="w= 0.8")
ax.plot(h1_vec3,v1_vec3, label="w= 0.5")
ax.grid()
ax.legend()
ax.set_xlabel('human capital $h_1$')
ax.set_ylabel('utility $v_1$')
ax.set_title('value of living in period 1')

```

She gets a higher utility for working, even if the wage is below the unemployment benefit.

# 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 start by defining all the symbols in the model, and turn on nice symbol-printing:

```python
#for nice printing
sm.init_printing(use_unicode=True)

#Define all the symbols of the model:
alpha = sm.symbols('alpha')
h = sm.symbols('h')
b = sm.symbols('b')
phi = sm.symbols('phi')
gamma = sm.symbols('gamma')
r= sm.symbols('r')
vt= sm.symbols('v_t')
yt= sm.symbols('y_t')
pi= sm.symbols('pi')
phi= sm.symbols('phi')
st= sm.symbols('s_t')
pit_1= sm.symbols('pi_t-1')
yt_1=sm.symbols('y_t-1')
st_1=sm.symbols('s_t-1')
```

Then, we define the AD- and SRAS-curves:

```python
#Define Equations:
AD= (1/(h*alpha))*(vt-(yt*(1+b*alpha)))
SRAS=pit_1+(gamma*yt)-(phi*gamma*yt_1)+st+(phi*st_1)

```

```python
AD #check that it worked
```

```python
SRAS #check that it worked
```

```python
AD1=(1/(par['h']*par['alpha']))*(vt-(yt*(1+par['b']*par['alpha'])))
AD1
```

```python
SRAS1=pit_1+(par['gamma']*yt)-(par['phi']*par['gamma']*yt_1)+st+(par['phi']*st_1)
SRAS1
```

The short term equilibrium is defined by aggregate supply (SRAS) equal to aggregate demand (AD). Therefore, we start by setting the two equations equal to each other, and solve for $y_t$ to find the equilibrium output. We will first find the general equilibrium expressions, and then we insert for the given parameter values to find the specific expression:

**Equilibrium output and inflation (general)**

```python
#find output:
AD_SRAS=sm.Eq(AD,SRAS)
ytstar=sm.solve(AD_SRAS, yt)
ytstar
```

We now want to solve for the equilibrium inflation, by inserting the obtained ystar into the AD-equation

```python
#find inflation
pitstar = 1/(h*alpha)*(vt-(1+b*alpha)*ytstar[0])
pitstar

```

**Equilibrium output and inflation (with the given parameter values)**

```python
#find output
AD1_SRAS1=sm.Eq(AD1,SRAS1)
ytstar1=sm.solve(AD1_SRAS1, yt)
ytstar1
```

```python
#find inflation
pitstar1 = 1/(par['h']*par['alpha'])*(vt-(1+par['b']*par['alpha'])*ytstar1[0])
pitstar1

```

We can see that the equilibrium output and inflation depends on inflation in the previous periods and supply- and demand disturbance.

# Question 2.2

We now want to solve for $y_t$ and $pi_t$ **numerically**.
First, we define the functions:

```python
yt_eq = sm.lambdify((alpha,h,b,phi,gamma,vt,st,pit_1,yt_1,st_1), ytstar[0])
```

```python
pit_eq = sm.lambdify((alpha,h,b,phi,gamma,vt,st,pit_1,yt_1,st_1), pitstar)
```

Then use that:

$$y_{t-1} = \pi_{t-1} = v_t = s_t = s_{t-1} = 0$$
$$ \alpha = 5.76 $$
$$ h = 0.5 $$
$$ b = 0.5 $$
$$ \phi = 0 $$
$$ \gamma = 0.075$$

##### using $v_t$ =0

**Output**

```python
yt_eq(par['alpha'], par['h'], par['b'], par['phi'], par['gamma'], 0, 0, 0, 0, 0)
```

**Inflation**

```python
pit_eq(par['alpha'], par['h'], par['b'], par['phi'], par['gamma'], 0, 0, 0, 0, 0)
```

Now we want to illustrate the solution **graphically**. We define the AD-and SRAS-curves as functions:

```python
def ADcurve(h, alpha, yt, vt, b):
    return (1/(h*alpha))*(vt-(y*(1+b*alpha)))

def SRAScurve(pit_1, gamma, yt, yt_1, st, phi, st_1):
    return pit_1+(gamma*yt)-(phi*gamma*yt_1)+st+(phi*st_1)
```

```python
#insert the given values in the funcitons
y=np.linspace(-0.1,0.1,100)
AD_plot=ADcurve(0.5, 5.76, y, 0, 0.5)
SRAS_plot=SRAScurve(0, 0.075, y, 0, 0, 0, 0)

#create the plot
plt.plot(y,AD_plot,label='AD')
plt.plot(y,SRAS_plot,label='SRAS')
plt.grid(True)

plt.title('AD and SRAS')
plt.xlabel('$y_t$')
plt.ylabel('$\pi_t$')
```

Graphically, the short term equilibrium is given by the intersection of the AD-curve and the SRAS-curve.
As seen in the figure, this gives $y_{t} = \pi_{t} $ =0, which is the same as our numerical solution.

#### Now, we do the same with $v_t$ =0.1

#### Numerically

```python
#output
yt_eq(par['alpha'], par['h'], par['b'], par['phi'], par['gamma'], 0.1, 0, 0, 0, 0)
```

```python
#inflation
pit_eq(par['alpha'], par['h'], par['b'], par['phi'], par['gamma'], 0.1, 0, 0, 0, 0)
```

**Graphically**

Increasning $v_t$ causes a positive shift in the AD-curve:

```python
#graph the shift
AD_plot1=ADcurve(0.5, 5.76, y, 0.1, 0.5)

y = np.linspace(-0.1,0.1,100)
plt.plot(y,AD_plot1,label='AD1')
plt.plot(y,AD_plot,label='AD0')
plt.grid(True)

plt.title('Positive shift in the AD-curve')
plt.xlabel('$y_t$')
plt.ylabel('$\pi_t$')

```

For a given value of $y_t$, $\pi_t$ is now higher than before the shift. Therefore, in the new equilibrium $y_t$ and $\pi_t $ will we higher:

```python
#graph the new equilibrium
AD_plot1=ADcurve(0.5, 5.76, y, 0.1, 0.5)

y = np.linspace(-0.1,0.1,100)
plt.plot(y,AD_plot1,label='AD0')
plt.plot(y,SRAS_plot,label='SRAS')
plt.grid(True)

plt.title('AD and SRAS')
plt.xlabel('$y_t$')
plt.ylabel('$\pi_t$')
```

We can see that the graphical solution corresponds to the numerical, with
$y_t$ = 0.0244 and $\pi_t$ = 0.0018

## Question 2.3

Fist, we have to define the AR(1) processes. We do this by creating the functions v_func and s_func

```python
#Define the AR(1) processes
def v_func(vt_1, x):
    return par['delta']*vt_1+x

def s_func(st_1,c):
    return par['omega']*st_1+c
```

```python
y_lambdified = sm.lambdify((pit_1, st, vt, phi, st_1, yt_1), ytstar1)
pi_lambdified = sm.lambdify((pit_1, st, yt, phi, st_1, yt_1), pitstar1)
```

We then want to run a simulation with T=50. We create empty vectors for the variables of interest, and fill them by looping through the time range

```python
T=50

#allocate simulation data
y = [0]
pi = [0]
v = [0]
s = [0]
c = np.zeros(T)
x = np.zeros(T)
x[1] = 0.1 #demand shock

for t in range(1,T):
    #update z and v
    v.append(v_func(v[t-1], x[t]))
    s.append(s_func(s[t-1], c[t]))

    #compute y and pi using yt_eq and pit_eq
    y.append(yt_eq(par['alpha'], par['h'],par['b'],par['phi'],par['gamma'],v[t],s[t],pi[t-1],y[t-1],s[t-1]))
    pi.append(pit_eq(par['alpha'], par['h'],par['b'],par['phi'],par['gamma'],v[t],s[t],pi[t-1],y[t-1],s[t-1]))

```

### Plot evolution of output gap over T=50 periods

```python
plot = plt.figure(figsize=(10,15))
ax = plot.add_subplot(311)
plt.grid(True)

ax.plot(y,label='$\y$')
ax.set_title('Output gap development')
ax.set_xlabel('$time$')
ax.set_ylabel('$y$')
```

The spike in the output gap comes from $x_0$ = 0.1. This represents a demand shock, which causes output to increase. The output gap stabalizes over time, due to the fact that $x_t$ =0.

### Plot evolution of inflation gap over T=50 periods

```python
plot = plt.figure(figsize=(10,15))
ax = plot.add_subplot(311)
plt.grid(True)

ax.plot(pi,label='$\pi$')
ax.set_title('Inflation gap development')
ax.set_xlabel('$time$')
ax.set_ylabel('$\pi$')
```

The interpretation is simlar to the previous. The spike in the inflation gap is caused by the demand shock, $x_0$ =0.1
We can also plot them in the same diagram:

```python
plot = plt.figure(figsize=(10,15))
ax = plot.add_subplot(311)

ax.plot(y,label='$y$')
ax.plot(pi,label='$\pi$')
ax.legend(loc='upper right');
plt.grid(True)

ax.set_xlabel('$time$')
ax.set_ylabel('$\pi/y$')
```

In the long run, they both converges towards 0.Fluctuations are caused by supply- and demand shocks.

# Question 2.4
### Simulate model

We start by simulate the AS-AD model for 1,000 periods.

```python
#set-up
np.random.seed(12345)
T=1000

#generate the stochastic shocks
x1 = np.random.normal(loc=0,scale=par['sigma_x'],size=T)
c1 = np.random.normal(loc=0,scale=par['sigma_c'],size=T)

#allocate simulation data
y1 = [0]
pi1 = [0]
v1 = [0]
s1 = [0]
x[1] = 0.1 #demand shock

for t in range(1,T):
    #update z and v
    v1.append(v_func(v1[t-1], x1[t]))
    s1.append(s_func(s1[t-1], c1[t]))

    #compute y and pi using yt_eq and pit_eq
    y1.append(yt_eq(par['alpha'], par['h'],par['b'],par['phi'],par['gamma'],v1[t],s1[t],pi1[t-1],y1[t-1],s1[t-1]))
    pi1.append(pit_eq(par['alpha'], par['h'],par['b'],par['phi'],par['gamma'],v1[t],s1[t],pi1[t-1],y1[t-1],s1[t-1]))

```

## Plot output gap and inflation development in the same graph

```python
plot = plt.figure(figsize=(10,15))
ax = plot.add_subplot(311)

ax.plot(y1,label='$output$')
ax.plot(pi1,label='$inflation$')

ax.set_title('Output gap and inflation development')
ax.set_xlabel('$time$')
ax.set_ylabel('$\pi/y$')
ax.legend(loc='upper right');
```

## Calculate statistics

1.Variance of $y_t$, $var(y_t)$**

```python
y_var=np.var(y1)
y_var
```

2.Variance of $\pi_t$, $var(\pi_t)$

```python
pi_var=np.var(pi1)
pi_var
```

3.Correlation between $y_t$ and $\pi_t$, $corr(y_t,\pi_t)$

```python
np.corrcoef(y1,pi1)
```

    array([[ 1.        , -0.17556771],
           [-0.17556771,  1.        ]])

4. Auto-correlation between $y_t$ and $y_{t-1}$, $corr(y_t,y_{t-1})$.

```python
#Define a function for autocorrelation
def autocorr(x, t=1):
    return np.corrcoef(np.array([x[:-t], x[t:]]))

#find the autocorrelation of yt and yt-1
autocorr(y1)
```

    array([[1.        , 0.78020678],
           [0.78020678, 1.        ]])

5. Auto-correlation between $\pi_t$ and $\pi_{t-1}$, $corr(\pi_t,\pi_{t-1})$

```python
autocorr(pi1)
```

    array([[1.       , 0.9720786],
           [0.9720786, 1.       ]])

```python

```

```python
print(f'Variance of y is, {y_var:.2}')
print(f'Variance of pi is, {pi_var:.2}')
print(f'Correlation between y and pi is, {np.corrcoef(y1,pi1)[0,1]:.2}')
print(f'Autocorrelation in pi is, {autocorr(y1)[0,1]:.2}')
print(f'Autocorrelation in y is, {autocorr(pi1)[0,1]:.2}')
```

    Variance of y is, 1.9
    Variance of pi is, 0.86
    Correlation between y and pi is, -0.18
    Autocorrelation in pi is, 0.78
    Autocorrelation in y is, 0.97

# Question 2.5

**Note**: parts of this solution is inspired by dsd and chh jmk

We want to define a function which returns the correlation between output and inflation. We define the function to take $ \phi $ (phi) as an input, such that we can loop over different values of $ \phi $

```python
def corr_func(phi):
    #set intitial values
    y = [0]
    pi = [0]
    v = [0]
    s = [0]
    s_1=[0]
    corr = []

    #define phi to be between 0 and 1
    phi_values = np.linspace(0,1,100)

    #loop over different values of phi
    for phi in phi_values:

        np.random.seed(12345)

        # define supply and demand shocks:
        s_x = np.random.normal(loc=0,scale=par['sigma_x'],size=1000)
        s_c = np.random.normal(loc=0,scale=par['sigma_c'],size=1000)

        #update shock-variables
        for x in s_x:
            v.append(par['delta']*v[-1] + x)

        for c in s_c:
            s.append(par['omega']*s[-1] + c)

        #update s_1, v and s
        s_1 = s[:-1]
        v = v[1:]
        s = s[1:]

        #zip the three vectors
        zip_= list(zip(v,s,s_1))

        #use the equilibrium equations defined in 2.2 to find output and inflation values:

        for a,b,c in zip_:
            y_upd =yt_eq(par['alpha'],par['h'],par['b'],phi,par['gamma'],a,b,pi[-1],y[-1],c)
            pi_upd=pit_eq(par['alpha'],par['h'],par['b'],phi,par['gamma'],a,b,pi[-1],y[-1],c)

            y.append(y_upd)
            pi.append(pi_upd)

        y = y[1:]
        pi = pi[1:]

        # find correlation between y and pi
        corr_y_pi = np.corrcoef(y,pi)[0,1]
        corr.append(corr_y_pi)

        # Here we reset the initial values:

        y = [0]
        pi = [0]
        v = [0]
        s = [0]

    return corr[0]
```

Now, we can insert for different values of $ \phi $ in the corr-function, to obtain the correlation coefficient. To check that it works, we can test the function for $ \phi = 0 $ and see if we get the same correlation coefficient as in 2.4

```python
corr_func(0)
```

This is the same as in 2.4

Now, we want to plot the correlation between $y$ and $\pi$ for different values of $ \phi $

```python
fig=plt.figure(figsize=(8,5))
ax = fig.add_subplot(1,1,1)
ax.plot(phi_values,corr)
ax.grid()
ax.set_xlabel('$\phi$')
ax.set_ylabel('$corr(y_t,\pi_t)$')
ax.set_title('Correlation for different values of $\phi$');
```

We can create a function which squares the difference between our correlation function (corr) and the desired value (0.31), and then we use a numerical optimizer to minimize the squared distance

```python
#define the function
obj = lambda phi: (corr_func(phi)-0.31)**2

# use scipy optimize
solution=optimize.minimize_scalar(obj,0, method='bounded',bounds=(0,1))
solution

```

         fun: 0.23655945306149767
     message: 'Solution found.'
        nfev: 25
      status: 0
     success: True
           x: 0.9999940391390134

$Corr(y_t, \pi_t)$ is equal to 0.31 when $\phi$=1

# Question 2.6

We use the same procedure as in 2.5, but we now define new function "statistics", which also takes sigma_x and sigma_c as inputs. We also let the function return all the wanted statistics (variances, correlation and autocorrelations), instead of just correlation as in 2.5.

```python
def statistics(phi_us, sigma_x_us, sigma_c_us):

    #set intitial values
    y = [0]
    pi = [0]
    v = [0]
    s = [0]
    s_1=[0]
    corr = []

    #define phi to be between 0 and 1
    phi_values = np.linspace(0,1,100)

    #loop over different values of phi
    for phi_us in phi_values:

        np.random.seed(12345)

        # define supply and demand shocks:
        s_x = np.random.normal(loc=0,scale=sigma_x_us,size=1000)
        s_c = np.random.normal(loc=0,scale=sigma_c_us,size=1000)

        #update shock-variables
        for x in s_x:
            v.append(par['delta']*v[-1] + x)

        for c in s_c:
            s.append(par['omega']*s[-1] + c)

        #update s_1, v and s
        s_1 = s[:-1]
        v = v[1:]
        s = s[1:]

        #zip the tree vectors
        zip_= list(zip(v,s,s_1))

        #use the equilibrium equations defined in 2.2 to find output and inflation values
        for i,x,p in zip_:
            y_upd =yt_eq(par['alpha'],par['h'],par['b'],phi_us,par['gamma'],i,x,pi[-1],y[-1],p)
            pi_upd=pit_eq(par['alpha'],par['h'],par['b'],phi_us,par['gamma'],i,x,pi[-1],y[-1],p)

            y.append(y_upd)
            pi.append(pi_upd)

        y = y[1:]
        pi = pi[1:]

        # find correlation between y and pi
        corr_y_pi = np.corrcoef(y,pi)[0,1]
        corr.append(corr_y_pi)

        #define the different statistics
        var_y = np.var(y)
        var_pi = np.var(pi)
        corr_y_pi = np.corrcoef(y,pi)[0,1]
        autocorr_y = np.corrcoef(y[:-1],y[1:])[1,0]
        autocorr_pi= np.corrcoef(pi[:-1],pi[1:])[1,0]
        return [var_y, var_pi, corr_y_pi, autocorr_y, autocorr_pi]

```

Once again, we can check that the code work by test the function for $ \phi = 0 $ and see if we get the same correlation coefficient as in 2.4

```python
statistics(0, par['sigma_x'], par['sigma_c'])
```

Which is the same as we found in 2.4. Therefore, we know that the function is working.

Now, we create a vector with the desired US-statistics:

```python
US_statistics=np.array([1.64, 0.21, 0.31, 0.84, 0.48])
```

Define an intitial guess, x0, which we set equal to the values obtained 2.5. We also define a vector of the parameters (in order to use it in the lambda function) and set their respective bounds:

```python
#initial guess:
x0=np.array([1,3.492, 0.2])

#vector
parameters=[phi, sigma_x, sigma_c]

#define phi to be between 0 and 1, and the shock variables between 0 and 10
bounds=[[0,1],[0,10],[0,10]]
```

Now, we define the objective function, and solve it with a numerical optimizer (scripy)

```python
#define objective
obj_2= lambda parameters: np.sum((statistics(parameters[0], parameters[1], parameters[2]) - US_statistics)**2)

# use scipy optimize
solution_2=optimize.minimize(obj_2,x0, bounds=bounds)
solution_2
```

          fun: 0.39886456683595756
     hess_inv: <3x3 LbfgsInvHessProduct with dtype=float64>
          jac: array([ 0.00000000e+00, -2.05391260e-07, -5.93969318e-07])
      message: b'CONVERGENCE: NORM_OF_PROJECTED_GRADIENT_<=_PGTOL'
         nfev: 36
          nit: 8
       status: 0
      success: True
            x: array([1.        , 3.39390972, 0.01830073])

This gives ut the optimal parameters of phi, sigma_x and sigma_c:

```python
print('The optimal value of phi is: '+ str(round(solution_2.x[0],3)))
print('The optimal value of sigma_x '+ str(round(solution_2.x[1],3)))
print('The optimal value of sigma_c:'+ str(round(solution_2.x[2],3)))

```

    The optimal value of phi is: 1.0
    The optimal value of sigma_x 3.394
    The optimal value of sigma_c:0.018

We now want to run the simulation with these obtained values to get the variances of $y$ and $\pi $, the correlation between $y$ and $\pi $ and the autocorrelations of $y$ and $\pi $

```python
#assign values
phi_sol=solution_2.x[0]
sigma_x_sol=solution_2.x[1]
sigma_c_sol=solution_2.x[2]
```

```python
#run simulation
sim_sol=statistics(phi_sol,sigma_x_sol, sigma_c_sol)
sim_sol
```

```python
print('The variance of y is: ' + str(round(sim_sol[0],2)))
print('The cariance of pi is: ' + str(round(sim_sol[1],2)))
print('The correlation between y and pi is: ' + str(round(sim_sol[2],2)))
print('The autocorrelation of y is: ' + str(round(sim_sol[3],2)))
print('The autocorrelation of pi is: ' + str(round(sim_sol[4],2)))
```

    The variance of y is: 1.56
    The cariance of pi is: 0.47
    The correlation between y and pi is: 0.07
    The autocorrelation of y is: 0.75
    The autocorrelation of pi is: 0.99

#### Our solution:

1. $var(y_t) = 1.56$
2. $var(\pi_t) = 0.47$
3. $corr(y_t,\pi_t) = 0.07$
4. $corr(y_t,y_{t-1}) = 0.75$
5. $corr(\pi_t,\pi_{t-1}) = 0.99$

#### Desired solution:

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$

This is as close as we get with this approach.

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

**The answer to this question is mostly based on Lecture 2, 3, 4, 10 and Problem Set 2.**

# Question 1.1: Plot the histograms of the budget shares for each good across agents.

Inserting the income function into the implied demand function:

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

The equilibrium is a set of prices \\( p_1 \\), \\( p_2 \\) and \\(p_3\\) = 1 (numeraire good) satifying:

\\[
\begin{eqnarray*}
x_1(p_1,p_2) = \sum_{j=1}^N x_{1}^{\star j}(p_{1},p_{2},e^{j}) &=& \sum_{j=1}^N \beta_1^j \frac{p_1 e_1^j + p_2 e_2^j+ e_3^j}{p_1}\\
x_2(p_1,p_2) = \sum_{j=1}^N x_{2}^{\star j}(p_{1},p_{2},e^{j}) &=& \sum_{j=1}^N \beta_2^j \frac{p_1 e_1^j + p_2 e_2^j+ e_3^j}{p_2}\\
x_3(p_1,p_2) = \sum_{j=1}^N x_{3}^{\star j}(p_{1},p_{2},e^{j}) &=& \sum_{j=1}^N \beta_3^j (p_1 e_1^j + p_2 e_2^j+ e_3^j)
\end{eqnarray*}
\\]

Rearrainging the implied demand function to:
$$
\begin{eqnarray*}
x_{i}^{\star j}(p_{1},p_{2},\boldsymbol{e}^{j})&=&\beta^{j}_i\frac{I^j}{p_{i}} \\
\end{eqnarray*}
$$

$$
\begin{eqnarray*}
x_{i}^{\star j} p_{i} &=& \beta_i^j I^j \\
\end{eqnarray*}
$$

shows that $\beta$ is the fraction of consumer j's income share for good i.

```python
print(betas) #gives us an idea of the betas for all 50000 consumers
```

    [[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
type(betas)
```

    numpy.ndarray

```python
#extracting the first, second and third column of the betas goves us the betas for the respective good 1, 2 and 3
beta1 = betas[:,0]
beta2 = betas[:,1]
beta3 = betas[:,2]
print(beta1) #to check
```

    [0.53104511 0.50866997 0.83317207 ... 0.63725404 0.78205036 0.65552751]

```python
# Histograms

plt.hist(beta1, bins=100) #100 bins means grouping into 100 sections between 0 and 1
plt.title("Plot of histograms for budget share for good 1 across consumers")
plt.xlabel("Budget Share")
plt.ylabel("N")
plt.show()

plt.hist(beta2, bins=100) #100 bins means grouping into 100 sections between 0 and 1
plt.title("Plot of histograms for budget share for good 2 across consumers")
plt.xlabel("Budget Share")
plt.ylabel("N")
plt.show()

plt.hist(beta3, bins=100) #100 bins means grouping into 100 sections between 0 and 1
plt.title("Plot of histograms for budget share for good 3 across consumers")
plt.xlabel("Budget Share")
plt.ylabel("N")
plt.show()
```

It can be seen that good 1 has the highest budget share, followed by good 2 and then 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 3.2: Plot 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 = 0 $$

If the excess demand equals 0, Walras's law is fulfilled.

But first, we define the demand for each good's market dependent on \\(p_1\\) and \\(p_2\\).

```python
p3 = 1 #define the price of the numeraire good

def demand1_EE(betas,p1,p2,e1,e2,e3):
    I = p1*e1 + p2*e2 + e3
    return beta1*(I/p1)

def demand2_EE(betas,p1,p2,e1,e2,e3):
    I = p1*e1 + p2*e2 + e3
    return beta2*(I/p2)

def demand3_EE(betas,p1,p2,e1,e2,e3):
    I = p1*e1 + p2*e2 + e3
    return beta3*(I/p3)
```

Walras's law constitutes that if all other markets are in an equilibrium, the last one has to be, too. Therefore, solving 2 markets should result in the 3rd one not having any excess demand.

```python
#defining the excess demand functions for all goods
def excess_demand1_EE(betas,p1,p2,e1,e2,e3):

    demand_EE = np.sum(demand1_EE(betas,p1,p2,e1,e2,e3))  #sum of all consumers demand for good 1

    supply_EE = np.sum(e1) #sum of all endowments of good 1

    excess_demand_EE = demand_EE - supply_EE

    return excess_demand_EE

def excess_demand2_EE(betas,p1,p2,e1,e2,e3):

    demand_EE = np.sum(demand2_EE(betas,p1,p2,e1,e2,e3))

    supply_EE = np.sum(e2)

    excess_demand_EE = demand_EE - supply_EE

    return excess_demand_EE

def excess_demand3_EE(betas,p1,p2,e1,e2,e3):

    demand_EE = np.sum(demand3_EE(betas,p1,p2,e1,e2,e3))

    supply_EE = np.sum(e3)

    excess_demand_EE = demand_EE - supply_EE

    return excess_demand3_EE
```

Since \\(p_3 = 1\\) is set, we have \\(p_1\\) and \\(p_2\\) as the variables on the x and y axis of the 3D graph. The z axsis represents the excess demand for the respective.

```python
#create the vectors / grids
p1_vec = np.linspace(1,10,100) #create a linear vector in the interval [1,10] with 100 evenly spaced points
p2_vec = np.linspace(1,10,100)

#we need empty grids for the excess demand 1 and 2
e_demand1_grid_EE = np.empty((100,100))
e_demand2_grid_EE = np.empty((100,100))
p1_grid,p2_grid = np.meshgrid(p1_vec,p2_vec,indexing='ij')

for i,p1 in enumerate(p1_vec): # for loop in order to createthe excess demand in terms of the two price vectors
    for j,p2 in enumerate(p2_vec):
        e_demand1_grid_EE[i,j] = excess_demand1_EE(betas,p1,p2,e1,e2,e3)
        e_demand2_grid_EE[i,j] = excess_demand2_EE(betas,p1,p2,e1,e2,e3)
```

```python
# Figure
fig = plt.figure(figsize=(16,8))

ax1 = fig.add_subplot(121, projection='3d')
fig1 = ax1.plot_surface(p1_grid,p2_grid,e_demand1_grid_EE)
ax1.invert_xaxis()
ax1.set_title('Excess Demand Good 1')
ax1.set_xlabel('$p_1$')
ax1.set_ylabel('$p_2$')

ax2 = fig.add_subplot(122, projection='3d')
ax2.plot_surface(p1_grid,p2_grid,e_demand2_grid_EE)
ax2.invert_xaxis()
ax2.set_title('Excess Demand Good 2')
ax2.set_xlabel('$p_1$')
ax2.set_ylabel('$p_2$')

plt.show()
```

This result can be explained intuitively as well. A high price for good 1 combined with a low price for good 2 results in a high excess demand for good 1 and vice versa. The same can be applied to excess demand for good 2.

# Quesiton 3.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
#step 1: Guess on p1 and p2, tolerance epsilon and adjustment parameter kappa
p1 = 0.5
p2 = 0.5
kappa = 0.8
eps = 1e-8

def equilibrium_EE(betas,p1,p2,e1,e2,e3,kappa=0.8,eps=1e-8,maxiter=10000):

    t = 0
    while True:

        #step 2: Define the equilibrium function incl. the excess demand functions for good 1 and 2
        Z1 = excess_demand1_EE(betas,p1,p2,e1,e2,e3)
        Z2 = excess_demand2_EE(betas,p1,p2,e1,e2,e3)

        #step 3: define whether to stop or not
        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

        # step 4: change p's if not stopping
        p1 = p1 + kappa*Z1/N
        p2 = p2 + kappa*Z2/N

        # define how much of the process is displayed
        if t < 5 or t%1000 == 0:
            print(f'{t:3d}: p1 = {p1:12.8f} -> excess demand -> {Z1:14.8f}')
            print(f'{t:3d}: p2 = {p1:12.8f} -> excess demand -> {Z2:14.8f}')
        elif t == 5:
            print('   ...')

        t += 1

    return p1, p2
```

```python
# call the equilibrium function
p1, p2 = equilibrium_EE(betas,p1,p2,e1,e2,e3,kappa=kappa,eps=eps)
```

      0: p1 =   1.75618758 -> excess demand -> 78511.72403675
      0: p2 =   1.75618758 -> excess demand ->  1976.77594850
      1: p1 =   1.91889553 -> excess demand -> 10169.24631720
      1: p2 =   1.91889553 -> excess demand -> 30068.91485501
      2: p1 =   2.17170082 -> excess demand -> 15800.33081485
      2: p2 =   2.17170082 -> excess demand ->   331.86779583
      3: p1 =   2.36310422 -> excess demand -> 11962.71228988
      3: p2 =   2.36310422 -> excess demand ->  3333.01765190
      4: p1 =   2.52741831 -> excess demand -> 10269.63063665
      4: p2 =   2.52741831 -> excess demand ->  3630.14563580
       ...
    1000: p1 =   6.49005422 -> excess demand ->     0.00085602
    1000: p2 =   6.49005422 -> excess demand ->     0.00031934
    1705: p1 =   6.49005515 -> excess demand ->     0.00000003
    1705: p2 =   2.61669400 -> excess demand ->     0.00000001

```python
#Check whether excess demand is actually close to zero
Z1 = excess_demand1_EE(betas,p1,p2,e1,e2,e3)
Z2 = excess_demand2_EE(betas,p1,p2,e1,e2,e3)
print(Z1,Z2)
```

    2.6746420189738274e-08 9.975337889045477e-09

```python
#the equilibrium prices are
p1, p2
```

    (6.490055145458315, 2.616694000470743)

```python
#for the prupose of later comparison, the prices are named differently
p1_old = p1
p2_old = p2
```

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

(Additional orientation Exercise 1)

```python
#defining the utility function
def utlity_func_EE(beta1,beta2,beta3,p1,p2,e1,e2,e3,gamma):
    I = p1*e1 + p2*e2 + e3
    return (((beta1*(I/p1))**beta1)*((beta2*(I/p2))**beta2)*((beta3*I)**beta3))**gamma
```

```python
#defining the utility vector - except endowments all known
utility_vec_EE = utlity_func_EE(beta1,beta2,beta3,p1,p2,e1,e2,e3,gamma)

print(utility_vec_EE)
```

    [0.39804894 0.94209408 1.84280866 ... 0.9075745  1.07227294 1.34035517]

```python
#plotting the distribution of utility as histogram (visualising the distribution)
fig_distr_utility = plt.figure(figsize=(5,5))

plt.hist(utility_vec_EE, bins=100) #100 bins means grouping into 100 sections between 0 and 1
plt.title("Distribution of Utility")
plt.xlabel("Utility")
plt.ylabel("N") # N refers to the number of consumers
plt.show()
```

```python
#mean
mean = np.mean(utility_vec_EE)

#variance
var = np.var(utility_vec_EE)

print("The mean is ", round(mean,3), " and the variance is ", round(var,3), ".")
```

    The mean is  1.01  and the variance is  0.317 .

# 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
#define the endowments as equally distributed

e1 = np.random.uniform(zeta,size=N)
e2 = np.random.uniform(zeta,size=N)
e3 = np.random.uniform(zeta,size=N)
```

```python
"""Redo all the steps as previously when calculating the Walras Equilibrium"""

#step 1: Guess on p1 and p2, tolerance epsilon and adjustment parameter kappa
p1 = 0.5
p2 = 0.5
kappa = 0.8
eps = 1e-8

def equilibrium2_EE(betas,p1,p2,e1,e2,e3,kappa=0.8,eps=1e-8,maxiter=10000):

    t = 0
    while True:

        #step 2: Define the equilibrium function incl. the excess demand functions for good 1 and 2
        Z1 = excess_demand1_EE(betas,p1,p2,e1,e2,e3)
        Z2 = excess_demand2_EE(betas,p1,p2,e1,e2,e3)

        #step 3: define whether to stop or not
        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

        # step 4: change p's if not stopping
        p1 = p1 + kappa*Z1/N
        p2 = p2 + kappa*Z2/N

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

        t += 1

    return p1, p2
```

```python
# call find equilibrium function

p1, p2 = equilibrium_EE(betas,p1,p2,e1,e2,e3,kappa=kappa,eps=eps)

```

      0: p1 =   1.75530416 -> excess demand -> 78456.50975081
      0: p2 =   1.75530416 -> excess demand ->  1619.49434941
      1: p1 =   1.91580678 -> excess demand -> 10031.41422554
      1: p2 =   1.91580678 -> excess demand -> 30514.77399013
      2: p1 =   2.16983440 -> excess demand -> 15876.72579924
      2: p2 =   2.16983440 -> excess demand ->     8.04497900
      3: p1 =   2.36064982 -> excess demand -> 11925.96413845
      3: p2 =   2.36064982 -> excess demand ->  3235.38650474
      4: p1 =   2.52417695 -> excess demand -> 10220.44540570
      4: p2 =   2.52417695 -> excess demand ->  3586.86588124
       ...
    1000: p1 =   6.44732586 -> excess demand ->     0.00073970
    1000: p2 =   6.44732586 -> excess demand ->     0.00027504
    1688: p1 =   6.44732665 -> excess demand ->     0.00000003
    1688: p2 =   2.59082037 -> excess demand ->     0.00000001

```python
p1, p1_old, p2, p2_old
```

    (6.447326650556338, 6.490055145458315, 2.5908203659970717, 2.616694000470743)

The prices for the goods with uniformaly distributed endowments are minimally lower thanwith exponentially distributed endowments.

```python
Z1, Z2 #just to check
```

    (2.6746420189738274e-08, 9.975337889045477e-09)

Since the excess demand functions are close to zero, the result is credible.

**Plotting the distribution of utility:**

```python
#vector of utility dependent on uniformly distributed endowments
utility_vec2_EE = utlity_func_EE(beta1,beta2,beta3,p1,p2,e1,e2,e3,gamma)

print(utility_vec2_EE)
```

    [1.00621831 1.01188326 1.02308807 ... 1.00221399 1.01217551 1.00007905]

```python
plt.hist(utility_vec_EE, 100, label='for exponentially distributed endowments')
plt.hist(utility_vec2_EE, 100, label='for uniformly distributed endowments')
plt.xlabel('Utility')
plt.ylabel('N')
plt.title('Utility Distribution')
plt.legend()
```

```python
#mean
mean_new = np.mean(utility_vec2_EE)

#variance
var_new = np.var(utility_vec2_EE)

print("The old mean is ", round(mean,3), " and the new mean is ", round(mean_new,3), ".")
print("The old variance is ", round(var,3), " and the new variance is ", round(var_new,5), ".")
```

    The old mean is  1.01  and the new mean is  1.012 .
    The old variance is  0.317  and the new variance is  0.00016 .

From the plot displaying both distributions, it can be seen that the variance for the uniformly distributed case is a lot lower than for the exponentially distributed one.
The new utility function has a higher mean and a lot lower variance than before.

Now: does the value of \\(\gamma\\) play a role?
We conduct two new calculations with a different value for gamma.

```python
#new value for gamma
gamma = 0.2

utility_vec_new_EE = utlity_func_EE(beta1,beta2,beta3,p1_old,p2_old,e1,e2,e3,gamma)
utility_vec2_new_EE = utlity_func_EE(beta1,beta2,beta3,p1,p2,e1,e2,e3,gamma)

#calculating the new means
mean_newgamma = np.mean(utility_vec_new_EE) #mean for the original endowments with new gamma

mean_new_newgamma = np.mean(utility_vec2_new_EE) #mean for the equally distributed endowments with new gamma

print("The mean for exponentially distributed endowments for gamma = 0.3 is ", round(mean,3), " and for gamma = 0.2 is ", round(mean_newgamma,3), ".")
print("The mean for uniformly distributed endowments for gamma = 0.3 is ", round(mean_new,5), " and for gamma = 0.2 is ", round(mean_new_newgamma,5), ".")
```

    The mean for exponentially distributed endowments for gamma = 0.3 is  1.01  and for gamma = 0.2 is  1.012 .
    The mean for uniformly distributed endowments for gamma = 0.3 is  1.01155  and for gamma = 0.2 is  1.01155 .

When inserting different values for gamma, it can be seen that the mean for the original function with exponentially distributed endowments changes a bit and the mean for the utility with uniformly distributed endowments changes more. Therfore, we can conclude that the value of gamma plays a role in the conclusion.