You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: lectures/ifp_advanced.md
+60-27Lines changed: 60 additions & 27 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -127,7 +127,7 @@ does not grow too quickly.
127
127
128
128
When $\{R_t\}$ was constant we required that $\beta R < 1$.
129
129
130
-
Now it is stochastic, we require that
130
+
Since it is now stochastic, we require that
131
131
132
132
```{math}
133
133
:label: fpbc2
@@ -139,7 +139,7 @@ G_R := \lim_{n \to \infty}
139
139
```
140
140
141
141
Notice that, when $\{R_t\}$ takes some constant value $R$, this
142
-
reduces to the previous restriction $\beta R < 1$
142
+
reduces to the previous restriction $\beta R < 1$.
143
143
144
144
The value $G_R$ can be thought of as the long run (geometric) average
145
145
gross rate of return.
@@ -414,16 +414,26 @@ class IFP:
414
414
415
415
Here's the Coleman-Reffett operator based on EGM:
416
416
417
+
### Implementation Details
418
+
419
+
The implementation of operator $K$ maps directly to equation {eq}`k_opr`.
420
+
421
+
The left side $u'(\xi)$ becomes `u_prime_inv(β * Ez)` after solving for $\xi$.
422
+
423
+
The expectation term $\mathbb E_z \hat{R} (u' \circ \sigma)[\hat{R}(a - \xi) + \hat{Y}, \hat{Z}]$ is computed via Monte Carlo averaging over future states and shocks.
424
+
425
+
The max with $u'(a)$ is handled implicitly—the endogenous grid method naturally handles the liquidity constraint since we only solve for interior consumption where $c < a$.
426
+
417
427
```{code-cell} ipython
418
428
@jit
419
-
def K(a_in, σ_in, ifp):
429
+
def K(ae_vals, c_vals, ifp):
420
430
"""
421
431
The Coleman--Reffett operator for the income fluctuation problem,
422
432
using the endogenous grid method.
423
433
424
434
* ifp is an instance of IFP
425
-
* a_in[i, z] is an asset grid
426
-
* σ_in[i, z] is consumption at a_in[i, z]
435
+
* ae_vals[i, z] is an asset grid
436
+
* c_vals[i, z] is consumption at ae_vals[i, z]
427
437
"""
428
438
429
439
# Simplify names
@@ -433,12 +443,12 @@ def K(a_in, σ_in, ifp):
433
443
n = len(P)
434
444
435
445
# Create consumption function by linear interpolation
436
-
σ = lambda a, z: np.interp(a, a_in[:, z], σ_in[:, z])
446
+
σ = lambda a, z: np.interp(a, ae_vals[:, z], c_vals[:, z])
437
447
438
448
# Allocate memory
439
-
σ_out = np.empty_like(σ_in)
449
+
c_out = np.empty_like(c_vals)
440
450
441
-
# Obtain c_i at each s_i, z, store in σ_out[i, z], computing
451
+
# Obtain c_i at each s_i, z, store in c_out[i, z], computing
442
452
# the expectation term by Monte Carlo
443
453
for i, s in enumerate(s_grid):
444
454
for z in range(n):
@@ -452,20 +462,28 @@ def K(a_in, σ_in, ifp):
452
462
U = u_prime(σ(R_hat * s + Y_hat, z_hat))
453
463
Ez += R_hat * U * P[z, z_hat]
454
464
Ez = Ez / (len(η_draws) * len(ζ_draws))
455
-
σ_out[i, z] = u_prime_inv(β * Ez)
465
+
c_out[i, z] = u_prime_inv(β * Ez)
456
466
457
467
# Calculate endogenous asset grid
458
-
a_out = np.empty_like(σ_out)
468
+
ae_out = np.empty_like(c_out)
459
469
for z in range(n):
460
-
a_out[:, z] = s_grid + σ_out[:, z]
470
+
ae_out[:, z] = s_grid + c_out[:, z]
461
471
462
472
# Fixing a consumption-asset pair at (0, 0) improves interpolation
463
-
σ_out[0, :] = 0
464
-
a_out[0, :] = 0
473
+
c_out[0, :] = 0
474
+
ae_out[0, :] = 0
465
475
466
-
return a_out, σ_out
476
+
return ae_out, c_out
467
477
```
468
478
479
+
### Code Walkthrough
480
+
481
+
The operator creates a consumption function `σ` by interpolating the input policy, then uses triple nested loops to compute the expectation via Monte Carlo averaging over savings grid points, current states, future states, and shock realizations.
482
+
483
+
After computing optimal consumption $c_i$ at each savings level $s_i$ by inverting marginal utility, we construct the endogenous asset grid using $a_i = s_i + c_i$.
484
+
485
+
Setting consumption and assets to zero at the origin ensures smooth interpolation near zero assets, where the household consumes everything.
486
+
469
487
The next function solves for an approximation of the optimal consumption policy via time iteration.
470
488
471
489
```{code-cell} ipython
@@ -497,12 +515,24 @@ def solve_model_time_iter(model, # Class with model information
497
515
return a_new, σ_new
498
516
```
499
517
518
+
This function implements fixed-point iteration by repeatedly applying the operator $K$ until the policy converges.
519
+
520
+
Convergence is measured by the maximum absolute change in consumption across all states.
521
+
522
+
The operator is guaranteed to converge due to the contraction property discussed earlier.
523
+
500
524
Now we are ready to create an instance at the default parameters.
501
525
502
526
```{code-cell} ipython
503
527
ifp = IFP()
504
528
```
505
529
530
+
The default parameters represent a calibration with moderate risk aversion ($\gamma = 1.5$, CRRA utility) and a quarterly discount factor ($\beta = 0.96$, corresponding to roughly 4% annual discounting).
531
+
532
+
The Markov chain has high persistence (90% probability of staying in the current state), while returns have 10% volatility around a zero mean log return ($a_r = 0.1$, $b_r = 0.0$).
533
+
534
+
Labor income is state-dependent: $Y_t = \exp(0.2 \eta_t + 0.5 Z_t)$ implies higher expected income in the good state ($Z_t = 1$) compared to the bad state ($Z_t = 0$).
535
+
506
536
Next we set up an initial condition, which corresponds to consuming all
507
537
assets.
508
538
@@ -537,8 +567,11 @@ Notice that we consume all assets in the lower range of the asset space.
537
567
538
568
This is because we anticipate income $Y_{t+1}$ tomorrow, which makes the need to save less urgent.
539
569
540
-
Can you explain why consuming all assets ends earlier (for lower values of
541
-
assets) when $z=0$?
570
+
Observe that consuming all assets ends earlier (at lower asset levels) when $z=0$ compared to $z=1$.
571
+
572
+
This occurs because expected future income is lower in the bad state ($z=0$), so the household begins precautionary saving at lower wealth levels.
573
+
574
+
In contrast, when $z=1$ (good state), higher expected future income allows the household to consume all assets up to a higher threshold before savings become optimal.
542
575
543
576
### Law of Motion
544
577
@@ -684,14 +717,14 @@ Here's the Coleman-Reffett operator using JAX:
684
717
685
718
```{code-cell} ipython
686
719
@jax_jit
687
-
def K_jax(a_in, σ_in, ifp):
720
+
def K_jax(ae_vals, c_vals, ifp):
688
721
"""
689
722
The Coleman--Reffett operator for the income fluctuation problem,
690
723
using the endogenous grid method with JAX.
691
724
692
725
* ifp is an instance of IFP_JAX
693
-
* a_in[i, z] is an asset grid
694
-
* σ_in[i, z] is consumption at a_in[i, z]
726
+
* ae_vals[i, z] is an asset grid
727
+
* c_vals[i, z] is consumption at ae_vals[i, z]
695
728
"""
696
729
697
730
# Extract parameters from ifp
@@ -701,9 +734,9 @@ def K_jax(a_in, σ_in, ifp):
701
734
n = len(P)
702
735
703
736
# Allocate memory
704
-
σ_out = jnp.empty_like(σ_in)
737
+
c_out = jnp.empty_like(c_vals)
705
738
706
-
# Obtain c_i at each s_i, z, store in σ_out[i, z], computing
739
+
# Obtain c_i at each s_i, z, store in c_out[i, z], computing
0 commit comments