Skip to content

Commit 50b7d77

Browse files
committed
misc
1 parent d6761d1 commit 50b7d77

File tree

1 file changed

+64
-74
lines changed

1 file changed

+64
-74
lines changed

lectures/ifp.md

Lines changed: 64 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,14 @@ u_prime_inv = lambda c, γ: c**(-1/γ)
349349

350350
### Solver
351351

352+
Here is the operator $K$ that transforms current guess $\sigma$ into next period
353+
guess $K\sigma$.
354+
355+
We understand $\sigma$ is an array of shape $(n_a, n_z)$, where $n_a$ and $n_z$
356+
are the respective grid sizes.
357+
358+
The value `σ[i,j]` corresponds to $\sigma(a'_i, z_j)$.
359+
352360
```{code-cell} ipython3
353361
def K(σ: jnp.ndarray, ifp: IFP) -> jnp.ndarray:
354362
"""
@@ -358,33 +366,21 @@ def K(σ: jnp.ndarray, ifp: IFP) -> jnp.ndarray:
358366
This operator implements one iteration of the EGM algorithm to
359367
update the consumption policy function.
360368
361-
Parameters
362-
----------
363-
σ : jnp.ndarray, shape (n_a, n_z)
364-
Current guess of consumption policy, σ[i, j] is consumption
365-
when assets = asset_grid[i] and income state = z_grid[j]
366-
ifp : IFP
367-
Model parameters
368-
369-
Returns
370-
-------
371-
σ_new : jnp.ndarray, shape (n_a, n_z)
372-
Updated consumption policy
373-
374369
Algorithm
375370
---------
376371
The EGM works backwards from next period:
377372
1. Given σ(a', z'), compute current consumption c that
378373
satisfies Euler equation
379-
2. Compute the endogenous current asset level a that leads
374+
2. Compute the endogenous current asset level a^e that leads
380375
to (c, a')
381-
3. Interpolate back to exogenous grid to get σ_new(a, z)
376+
3. Interpolate back to exogenous grid to get σ_new(a', z')
377+
382378
"""
383379
R, β, γ, Π, z_grid, asset_grid = ifp
384380
n_a = len(asset_grid)
385381
n_z = len(z_grid)
386382
387-
def compute_c_for_state(j):
383+
def compute_c_for_fixed_income_state(j):
388384
"""
389385
Compute updated consumption policy for income state z_j.
390386
@@ -416,9 +412,9 @@ def K(σ: jnp.ndarray, ifp: IFP) -> jnp.ndarray:
416412
417413
return σ_new # Consumption over the asset grid given z[j]
418414
419-
# Vectorize computation over all exogenous states using vmap
420-
# Resulting shape is (n_z, n_a), so one row per income state
421-
σ_new = jax.vmap(compute_c_for_state)(jnp.arange(n_z))
415+
# Compute consumption over all income states using vmap
416+
c_vmap = jax.vmap(compute_c_for_fixed_income_state)
417+
σ_new = c_vmap(jnp.arange(n_z)) # Shape (n_z, n_a), one row per income state
422418
423419
return σ_new.T # Transpose to get (n_a, n_z)
424420
```
@@ -444,11 +440,9 @@ def solve_model(ifp: IFP,
444440
error = jnp.max(jnp.abs(σ_new - σ))
445441
return i + 1, σ_new, error
446442
447-
# Initialize loop state
448443
initial_state = (0, σ_init, tol + 1)
449-
450-
# Run the loop
451-
i, σ, error = jax.lax.while_loop(condition, body, initial_state)
444+
final_loop_state = jax.lax.while_loop(condition, body, initial_state)
445+
i, σ, error = final_loop_state
452446
453447
return σ
454448
```
@@ -475,6 +469,45 @@ ax.legend()
475469
plt.show()
476470
```
477471

472+
To begin to understand the long run asset levels held by households under the default parameters, let's look at the
473+
45 degree diagram showing the law of motion for assets under the optimal consumption policy.
474+
475+
```{code-cell} ipython3
476+
ifp = create_ifp()
477+
R, β, γ, Π, z_grid, asset_grid = ifp
478+
σ_init = R * asset_grid[:, None] + y(z_grid)
479+
σ_star = solve_model(ifp, σ_init)
480+
a = asset_grid
481+
482+
fig, ax = plt.subplots()
483+
for z, lb in zip((0, 1), ('low income', 'high income')):
484+
ax.plot(a, R * (a - σ_star[:, z]) + y(z) , label=lb)
485+
486+
ax.plot(a, a, 'k--')
487+
ax.set(xlabel='current assets', ylabel='next period assets')
488+
489+
ax.legend()
490+
plt.show()
491+
```
492+
493+
The unbroken lines show the update function for assets at each $z$, which is
494+
495+
$$
496+
a \mapsto R (a - \sigma^*(a, z)) + y(z)
497+
$$
498+
499+
The dashed line is the 45 degree line.
500+
501+
The figure suggests that the dynamics will be stable --- assets do not diverge
502+
even in the highest state.
503+
504+
In fact there is a unique stationary distribution of assets that we can calculate by simulation -- we examine this below.
505+
506+
* Can be proved via theorem 2 of {cite}`HopenhaynPrescott1992`.
507+
* It represents the long run dispersion of assets across households when households have idiosyncratic shocks.
508+
509+
510+
478511
### A Sanity Check
479512

480513
One way to check our results is to
@@ -524,11 +557,12 @@ This looks pretty good.
524557

525558
Let's consider how the interest rate affects consumption.
526559

527-
* Step `r` through `np.linspace(0, 0.04, 4)`.
560+
* Step `r` through `np.linspace(0, 0.016, 4)`.
528561
* Other than `r`, hold all parameters at their default values.
529562
* Plot consumption against assets for income shock fixed at the smallest value.
530563

531-
Your figure should show that higher interest rates boost savings and suppress consumption.
564+
Your figure should show that, for this model, higher interest rates boost
565+
suppress consumption (because they encourage more savings).
532566

533567
```{exercise-end}
534568
```
@@ -541,7 +575,7 @@ Here's one solution:
541575

542576
```{code-cell} ipython3
543577
# With β=0.98, we need R*β < 1, so r < 0.0204
544-
r_vals = np.linspace(0, 0.015, 4)
578+
r_vals = np.linspace(0, 0.016, 4)
545579
546580
fig, ax = plt.subplots()
547581
for r_val in r_vals:
@@ -564,56 +598,12 @@ plt.show()
564598
:label: ifp_ex2
565599
```
566600

567-
Now let's consider the long run asset levels held by households under the
568-
default parameters.
569-
570-
The following figure is a 45 degree diagram showing the law of motion for assets when consumption is optimal
571-
572-
```{code-cell} ipython3
573-
ifp = create_ifp()
574-
R, β, γ, Π, z_grid, asset_grid = ifp
575-
σ_init = R * asset_grid[:, None] + y(z_grid)
576-
σ_star = solve_model(ifp, σ_init)
577-
a = asset_grid
578-
579-
fig, ax = plt.subplots()
580-
for z, lb in zip((0, 1), ('low income', 'high income')):
581-
ax.plot(a, R * (a - σ_star[:, z]) + y(z) , label=lb)
582-
583-
ax.plot(a, a, 'k--')
584-
ax.set(xlabel='current assets', ylabel='next period assets')
585-
586-
ax.legend()
587-
plt.show()
588-
```
589-
590-
The unbroken lines show the update function for assets at each $z$, which is
591-
592-
$$
593-
a \mapsto R (a - \sigma^*(a, z)) + y(z)
594-
$$
595-
596-
The dashed line is the 45 degree line.
597-
598-
We can see from the figure that the dynamics will be stable --- assets do not
599-
diverge even in the highest state.
600-
601-
In fact there is a unique stationary distribution of assets that we can calculate by simulation
602-
603-
* Can be proved via theorem 2 of {cite}`HopenhaynPrescott1992`.
604-
* It represents the long run dispersion of assets across households when households have idiosyncratic shocks.
605-
606-
Ergodicity is valid here, so stationary probabilities can be calculated by averaging over a single long time series.
607-
608-
Hence to approximate the stationary distribution we can simulate a long time
609-
series for assets and histogram it.
601+
Let's approximate the stationary distribution by simulation.
610602

611-
Your task is to generate such a histogram.
603+
Run a large number of households forward for $T$ periods and then histogram the
604+
cross-sectional distribution of assets.
612605

613-
* Use a single time series $\{a_t\}$ of length 500,000.
614-
* Given the length of this time series, the initial condition $(a_0,
615-
z_0)$ will not matter.
616-
* You might find it helpful to use the `MarkovChain` class from `quantecon`.
606+
Set `num_households=50_000, T=500`.
617607

618608
```{exercise-end}
619609
```

0 commit comments

Comments
 (0)