diff --git a/.translate/state/ifp_advanced.md.yml b/.translate/state/ifp_advanced.md.yml
new file mode 100644
index 00000000..ab4ef609
--- /dev/null
+++ b/.translate/state/ifp_advanced.md.yml
@@ -0,0 +1,6 @@
+source-sha: 60235ebbd73f767403ac39907ac68560fbb444ba
+synced-at: "2026-07-18"
+model: claude-sonnet-5
+mode: RESYNC
+section-count: 7
+tool-version: 0.17.0
diff --git a/lectures/ifp_advanced.md b/lectures/ifp_advanced.md
index 0d1cd3b9..7ca0b874 100644
--- a/lectures/ifp_advanced.md
+++ b/lectures/ifp_advanced.md
@@ -7,9 +7,39 @@ kernelspec:
display_name: Python 3
language: python
name: python3
+translation:
+ title: 收入波动问题 V:资产随机收益
+ headings:
+ Overview: 概述
+ The Model: 模型
+ The Model::Set Up: 设定
+ The Model::Assumptions: 假设
+ The Model::Optimality: 最优性
+ Solution Algorithm: 求解算法
+ Solution Algorithm::A Time Iteration Operator: 时间迭代算子
+ Solution Algorithm::Convergence Properties: 收敛性质
+ Solution Algorithm::Using an Endogenous Grid: 使用内生网格
+ Solution Algorithm::Using an Endogenous Grid::Finding Optimal Consumption: 寻找最优消费
+ Solution Algorithm::Using an Endogenous Grid::Iterating: 迭代
+ Implementation: 实现
+ Simulation: 模拟
+ Wealth Inequality: 财富不平等
+ Wealth Inequality::Measuring Inequality: 度量不平等
+ Exercises: 练习
---
-# 收入波动问题 II:资产随机收益
+```{raw} jupyter
+
+```
+
+# {index}`收入波动问题 V:资产随机收益 `
+
+```{include} _admonition/gpu.md
+```
```{contents} 目录
:depth: 2
@@ -17,7 +47,7 @@ kernelspec:
除了 Anaconda 中的内容外,本讲座还需要以下库:
-```{code-cell} ipython
+```{code-cell} ipython3
---
tags: [hide-output]
---
@@ -26,7 +56,7 @@ tags: [hide-output]
## 概述
-在本讲座中,我们继续研究 {doc}`收入波动问题 `。
+在本讲座中,我们继续研究 {doc}`ifp_egm` 中描述的收入波动问题。
之前假设利率是固定的,但现在我们允许资产收益随状态变化。
@@ -40,20 +70,24 @@ tags: [hide-output]
我们需要以下导入:
-```{code-cell} ipython
+```{code-cell} ipython3
import matplotlib.pyplot as plt
import matplotlib as mpl
FONTPATH = "fonts/SourceHanSerifSC-SemiBold.otf"
mpl.font_manager.fontManager.addfont(FONTPATH)
plt.rcParams['font.family'] = ['Source Han Serif SC']
-
import numpy as np
-from numba import jit, float64
-from numba.experimental import jitclass
-from quantecon import MarkovChain
+import quantecon as qe
+import jax
+import jax.numpy as jnp
+from jax import vmap
+from typing import NamedTuple
+from functools import partial
```
-## 储蓄问题
+
+
+## 模型
在本节中,我们回顾家庭问题及其最优性结果。
@@ -79,41 +113,37 @@ a_{t+1} = R_{t+1} (a_t - c_t) + Y_{t+1}
初始条件 $(a_0, Z_0)=(a,z)$ 视为给定。
-注意,财富的总收益率序列 ${R_t}_{t \geq 1}$ 允许是随机的。
+与 {doc}`intermediate:ifp_egm_transient_shocks` 唯一的不同之处在于,财富的总收益率 $\{R_t\}_{t \geq 1}$ 现在允许是随机的。
-序列 $\{Y_t \}_{t \geq 1}$ 是非金融收入。
-
-问题的随机成分服从
+具体而言,我们假设
```{math}
:label: eq:RY_func
-R_t = R(Z_t, \zeta_t)
- \quad \text{且} \quad
-Y_t = Y(Z_t, \eta_t),
+ R_t = R(Z_t, \zeta_t)
+ \quad \text{且} \quad
+ Y_t = Y(Z_t, \eta_t),
```
其中
-* 映射 $R$ 和 $Y$ 是时不变的非负函数,
+* $R$ 和 $Y$ 是时不变的非负函数,
* 创新过程 $\{\zeta_t\}$ 和 $\{\eta_t\}$ 独立同分布且相互独立,
-* $\{Z_t\}_{t \geq 0}$ 是有限集 $\mathsf Z$ 上的不可约齐次马尔可夫链
+* $\{Z_t\}_{t \geq 0}$ 是有限集 $\mathsf Z$ 上的马尔可夫链
令 $P$ 表示链 $\{Z_t\}_{t \geq 0}$ 的马尔可夫矩阵。
-我们对偏好的假设与 {doc}`之前的讲座 ` 中关于收入波动问题的假设相同。
-
-如前所述,$\mathbb E_z \hat X$ 表示给定当前值 $Z = z$ 时下一期值 $\hat X$ 的期望。
+在下文中,$\mathbb E_z \hat X$ 表示给定当前值 $Z = z$ 时下一期值 $\hat X$ 的期望。
### 假设
我们需要一些限制条件来确保目标 {eq}`trans_at` 是有限的,并且下面描述的解法能够收敛。
-我们还需要确保财富的现值值不会增长得太快。
+我们还需要确保财富的现值不会增长得太快。
当 $\{R_t\}$ 是常数时,我们要求 $\beta R < 1$。
-现在它是随机的,我们要求
+现在它是随机的,我们要求(参见 {cite}`ma2020income`)
```{math}
:label: fpbc2
@@ -124,25 +154,26 @@ G_R := \lim_{n \to \infty}
\left(\mathbb E \prod_{t=1}^n R_t \right)^{1/n}
```
-注意,当 $\{R_t\}$ 取某个常数值 $R$ 时,这一条件简化为之前的限制 $\beta R < 1$
-
值 $G_R$ 可以理解为长期(几何)平均总收益率。
-{cite}`ma2020income`提供了{eq}`fpbc2` 背后的更多直觉。
+为了简化本讲座,我们将*假设利率过程是独立同分布的*。
-我们在下面讨论如何检验该条件。
+在这种情况下,从 $G_R$ 的定义可以清楚地看出 $G_R$ 就是 $\mathbb E R_t$。
+
+我们在下面的代码中检验条件 $\beta \mathbb E R_t < 1$。
-最后,我们对非金融收入施加一些常规的技术性限制:
+最后,我们对非金融收入施加一些常规的技术性限制。
$$
\mathbb E \, Y_t < \infty \text{ 且 } \mathbb E \, u'(Y_t) < \infty
+\label{a:y0}
$$
一个相对简单且满足所有这些限制的环境是 {cite}`benhabib2015` 的独立同分布和 CRRA 环境。
### 最优性
-令候选消费政策类 $\mathscr C$ 的定义 {doc}`如前 `。
+令候选消费政策类 $\mathscr C$ 的定义如 {doc}`ifp_egm` 中所述。
在 {cite}`ma2020income` 中证明,在所述假设下,
@@ -162,15 +193,18 @@ $$
\right\}
```
-(直觉和推导与我们在 {doc}`早期讲座 ` 中关于收入波动问题的内容类似。)
+(直觉和推导与 {doc}`ifp_egm` 中的内容类似。)
-我们再次使用时间迭代来求解欧拉方程,使用 Coleman--Reffett 算子 $K$ 来匹配欧拉方程 {eq}`ifpa_euler`。
+我们再次使用时间迭代来求解欧拉方程,使用与欧拉方程 {eq}`ifpa_euler` 相匹配的 Coleman--Reffett 算子 $K$ 进行迭代。
## 求解算法
+```{index} single: Optimal Savings; Computation
+```
+
### 时间迭代算子
-我们对候选类 $\sigma \in \mathscr C$ 消费政策的定义与{doc}`之前关于收入波动问题的讲座 `中的定义相同。
+我们对候选类 $\sigma \in \mathscr C$ 消费政策的定义与 {doc}`ifp_egm` 中的定义相同。
对于固定的 $\sigma \in \mathscr C$ 和 $(a,z) \in \mathbf S$,函数 $K\sigma$ 在 $(a,z)$ 处的值 $K\sigma(a,z)$ 定义为满足以下方程的 $\xi \in (0,a]$
@@ -191,7 +225,7 @@ $K$ 背后的思想是,从定义可以看出,$\sigma \in \mathscr C$ 满足
### 收敛性质
-如前所述,我们在 $\mathscr C$ 上定义如下度量
+如前所述,我们在 $\mathscr C$ 上配以如下度量
$$
\rho(c,d)
@@ -212,7 +246,7 @@ $$
### 使用内生网格
-在研究该模型时,我们发现可以通过 {doc}`内生网格方法 ` 进一步加速时间迭代。
+在研究该模型时,我们发现可以通过 {doc}`内生网格方法 ` 进一步加速时间迭代。
我们将在这里使用相同的方法。
@@ -250,357 +284,540 @@ c_i =
#### 迭代
-一旦我们得到 ${s_i, c_i}$ 对,内生资产网格通过 $a_i = c_i + s_i$ 获得。
+一旦我们得到 $\{s_i, c_i\}$ 对,内生资产网格通过 $a_i = c_i + s_i$ 获得。
另外,在上面的讨论中我们固定了 $z \in \mathsf Z$,所以可以将其与 $a_i$ 配对。
-通过在每个 $z$ 上对 ${a_i, c_i}$ 插值,就可以得到政策 $(a,z) \mapsto \sigma(a,z)$ 的近似。
+通过在每个 $z$ 上对 $\{a_i, c_i\}$ 插值,就可以得到政策 $(a,z) \mapsto \sigma(a,z)$ 的近似。
在下面的内容中,我们使用线性插值。
-### 检验假设
-
-时间迭代的收敛性依赖于条件 $\beta G_R < 1$ 的满足。
-我们可以利用 $G_R$ 等于矩阵 $L$ 的谱半径这一事实来检验。矩阵 $L$ 定义为
+## 实现
-$$
-L(z, \hat z) := P(z, \hat z) \int R(\hat z, x) \phi(x) dx
-$$
+以下是以 `NamedTuple` 表示的模型。
-这个恒等式在 {cite}`ma2020income` 中得到证明,其中 $\phi$ 是资产收益创新 $\zeta_t$ 的密度函数。
+```{code-cell} ipython3
+class IFP(NamedTuple):
+ """
+ 一个 NamedTuple,使用 JAX 存储收入波动问题的基本参数。
+ """
+ γ: float
+ β: float
+ P: jnp.ndarray
+ a_r: float
+ b_r: float
+ a_y: float
+ b_y: float
+ s_grid: jnp.ndarray
+ η_draws: jnp.ndarray
+ ζ_draws: jnp.ndarray
+
+
+def create_ifp(
+ γ=1.5, # 效用参数
+ β=0.96, # 折现因子
+ P=jnp.array([(0.9, 0.1), # Z 的默认马尔可夫链
+ (0.1, 0.9)]),
+ a_r=0.16, # R 冲击中的波动率项
+ b_r=0.0, # R 冲击的均值偏移
+ a_y=0.2, # Y 冲击中的波动率项
+ b_y=0.5, # Y 冲击的均值偏移
+ shock_draw_size=100, # 用于蒙特卡洛
+ grid_max=100, # 外生网格最大值
+ grid_size=100, # 外生网格大小
+ seed=1234 # 随机种子
+ ):
+ """
+ 使用给定参数创建一个 IFP 实例。
-(注意,$\mathsf Z$ 是一个有限集,所以这个表达式定义了一个矩阵。)
+ """
+ # 假设 {R_t} 独立同分布且 ln R ~ N(b_r, a_r),检验稳定性
+ ER = np.exp(b_r + a_r**2 / 2)
+ assert β * ER < 1, "稳定性条件不成立。"
-当 $\{R_t\}$ 是独立同分布时,检查这一条件甚更容易。
+ # 使用 JAX 生成随机抽取
+ key = jax.random.PRNGKey(seed)
+ subkey1, subkey2 = jax.random.split(key)
+ η_draws = jax.random.normal(subkey1, (shock_draw_size,))
+ ζ_draws = jax.random.normal(subkey2, (shock_draw_size,))
+ s_grid = jnp.linspace(0, grid_max, grid_size)
-在这种情况下,从 $G_R$ 的定义可以清楚地看出 $G_R$ 就是 $\mathbb E R_t$。
+ return IFP(
+ γ, β, P, a_r, b_r, a_y, b_y, s_grid, η_draws, ζ_draws
+ )
-我们在下面的代码中检验条件 $\beta \mathbb E R_t < 1$。
-## 实现
+def u_prime(c, γ):
+ """边际效用"""
+ return c**(-γ)
-我们将假设 $R_t = \exp(a_r \zeta_t + b_r)$,其中 $a_r, b_r$ 是常数,$\{\zeta_t\}$ 是独立同分布的标准正态。
+def u_prime_inv(c, γ):
+ """边际效用的逆函数"""
+ return c**(-1/γ)
-我们允许劳动收入相关,即
+def R(z, ζ, a_r, b_r):
+ """资产的总收益率"""
+ return jnp.exp(a_r * ζ + b_r)
-$$
-Y_t = \exp(a_y \eta_t + Z_t b_y)
-$$
+def Y(z, η, a_y, b_y):
+ """劳动收入"""
+ return jnp.exp(a_y * η + (z * b_y))
+```
-其中 $\{\eta_t\}$ 也是独立同分布的标准正态,$\{ Z_t\}$ 是取值于 $\{0, 1\}$ 的马尔可夫链。
-
-```{code-cell} ipython
-ifp_data = [
- ('γ', float64), # 效用参数
- ('β', float64), # 折现因子
- ('P', float64[:, :]), # z_t 的转移概率
- ('a_r', float64), # R_t 的尺度参数
- ('b_r', float64), # R_t 的加性参数
- ('a_y', float64), # Y_t 的尺度参数
- ('b_y', float64), # Y_t 的加性参数
- ('s_grid', float64[:]), # 储蓄网格
- ('η_draws', float64[:]), # 用于 MC 的创新 η 的抽取
- ('ζ_draws', float64[:]) # 用于 MC 的创新 ζ 的抽取
-]
-```
-
-```{code-cell} ipython
-@jitclass(ifp_data)
-class IFP:
- """
- 用于收入波动问题的基本类
- """
+这是使用 JAX 的 Coleman-Reffett 算子:
- def __init__(self,
- γ=1.5,
- β=0.96,
- P=np.array([(0.9, 0.1),
- (0.1, 0.9)]),
- a_r=0.1,
- b_r=0.0,
- a_y=0.2,
- b_y=0.5,
- shock_draw_size=50,
- grid_max=10,
- grid_size=100,
- seed=1234):
-
- np.random.seed(seed) # 任意随机种子
-
- self.P, self.γ, self.β = P, γ, β
- self.a_r, self.b_r, self.a_y, self.b_y = a_r, b_r, a_y, b_y
- self.η_draws = np.random.randn(shock_draw_size)
- self.ζ_draws = np.random.randn(shock_draw_size)
- self.s_grid = np.linspace(0, grid_max, grid_size)
-
- # 假设 ${R_t}$ 独立同分布并服从下面给定的对数正态分布设定,进行稳定性检验。
- # 检验 β E R_t < 1。
- ER = np.exp(b_r + a_r**2 / 2)
- assert β * ER < 1, "稳定性条件不成立。"
-
- # 边际效用
- def u_prime(self, c):
- return c**(-self.γ)
-
- # 边际效用的逆函数
- def u_prime_inv(self, c):
- return c**(-1/self.γ)
-
- def R(self, z, ζ):
- return np.exp(self.a_r * ζ + self.b_r)
-
- def Y(self, z, η):
- return np.exp(self.a_y * η + (z * self.b_y))
-```
-
-这是基于 EGM 的 Coleman-Reffett 算子:
-
-```{code-cell} ipython
-@jit
-def K(a_in, σ_in, ifp):
+```{code-cell} ipython3
+def K(
+ a_in: jnp.array, # a_in[i, z] 是资产网格
+ c_in: jnp.array, # c_in[i, z] = a_in[i, z] 处的消费
+ ifp: IFP
+ ):
"""
- 收入波动问题的 Coleman--Reffett 算子,
- 使用内生网格方法。
+ 使用 JAX 结合内生网格方法的收入波动问题的
+ Coleman--Reffett 算子。
- * ifp 是 IFP 的实例
- * a_in[i, z] 是资产网格
- * σ_in[i, z] 是在 a_in[i, z] 处的消费
"""
- # 简化名称
- u_prime, u_prime_inv = ifp.u_prime, ifp.u_prime_inv
- R, Y, P, β = ifp.R, ifp.Y, ifp.P, ifp.β
- s_grid, η_draws, ζ_draws = ifp.s_grid, ifp.η_draws, ifp.ζ_draws
+ # 从 ifp 中提取参数
+ γ, β, P, a_r, b_r, a_y, b_y, s_grid, η_draws, ζ_draws = ifp
n = len(P)
- # 通过线性插值创建消费函数
- σ = lambda a, z: np.interp(a, a_in[:, z], σ_in[:, z])
-
- # 分配内存
- σ_out = np.empty_like(σ_in)
-
- # 在每个 s_i, z 处获得 c_i,存储在 σ_out[i, z] 中,
- # 通过蒙特卡洛计算期望项
- for i, s in enumerate(s_grid):
- for z in range(n):
- # 计算期望
- Ez = 0.0
- for z_hat in range(n):
- for η in ifp.η_draws:
- for ζ in ifp.ζ_draws:
- R_hat = R(z_hat, ζ)
- Y_hat = Y(z_hat, η)
- U = u_prime(σ(R_hat * s + Y_hat, z_hat))
- Ez += R_hat * U * P[z, z_hat]
- Ez = Ez / (len(η_draws) * len(ζ_draws))
- σ_out[i, z] = u_prime_inv(β * Ez)
-
+ def compute_expectation(s, z):
+ def inner_expectation(z_hat):
+ def compute_term(η, ζ):
+ R_hat = R(z_hat, ζ, a_r, b_r)
+ Y_hat = Y(z_hat, η, a_y, b_y)
+ a_val = R_hat * s + Y_hat
+ # 对消费进行插值
+ c_interp = jnp.interp(a_val, a_in[:, z_hat], c_in[:, z_hat])
+ mu = u_prime(c_interp, γ)
+ return R_hat * mu
+ # 对所有冲击组合进行向量化
+ η_grid, ζ_grid = jnp.meshgrid(η_draws, ζ_draws, indexing='ij')
+ terms = vmap(vmap(compute_term))(η_grid, ζ_grid)
+ return P[z, z_hat] * jnp.mean(terms)
+ # 对 z_hat 状态求和
+ Ez = jnp.sum(vmap(inner_expectation)(jnp.arange(n)))
+ return u_prime_inv(β * Ez, γ)
+
+ # 对 s_grid 和 z 进行向量化
+ compute_exp_v1 = vmap(compute_expectation, in_axes=(None, 0))
+ compute_exp_v2 = vmap(compute_exp_v1, in_axes=(0, None))
+ c_out = compute_exp_v2(s_grid, jnp.arange(n))
# 计算内生资产网格
- a_out = np.empty_like(σ_out)
- for z in range(n):
- a_out[:, z] = s_grid + σ_out[:, z]
+ a_out = s_grid[:, None] + c_out
+ # 在 (0, 0) 处固定消费-资产对
+ c_out = c_out.at[0, :].set(0)
+ a_out = a_out.at[0, :].set(0)
- # 在 (0, 0) 处固定消费-资产对有助于插值
- σ_out[0, :] = 0
- a_out[0, :] = 0
-
- return a_out, σ_out
+ return a_out, c_out
```
-下一个函数通过时间迭代求解最优消费政策的近似。
-
-```{code-cell} ipython
-def solve_model_time_iter(model, # 包含模型信息的类
- a_vec, # 资产的初始条件
- σ_vec, # 消费的初始条件
- tol=1e-4,
- max_iter=1000,
- verbose=True,
- print_skip=25):
-
- # 设置循环
- i = 0
- error = tol + 1
-
- while i < max_iter and error > tol:
- a_new, σ_new = K(a_vec, σ_vec, model)
- error = np.max(np.abs(σ_vec - σ_new))
+下一个函数使用 JAX 通过时间迭代求解最优消费政策的近似:
+
+```{code-cell} ipython3
+@jax.jit
+def solve_model(
+ ifp: IFP,
+ c_init: jnp.ndarray, # 内生网格上 σ 的初始猜测
+ a_init: jnp.ndarray, # 初始内生网格
+ tol: float = 1e-5,
+ max_iter: int = 1000
+ ) -> jnp.ndarray:
+ " 使用 EGM 的时间迭代求解模型。 "
+
+ def condition(loop_state):
+ c_in, a_in, i, error = loop_state
+ return (error > tol) & (i < max_iter)
+
+ def body(loop_state):
+ c_in, a_in, i, error = loop_state
+ c_out, a_out = K(c_in, a_in, ifp)
+ error = jnp.max(jnp.abs(c_out - c_in))
i += 1
- if verbose and i % print_skip == 0:
- print(f"第{i}迭代的误差是 {error}。")
- a_vec, σ_vec = np.copy(a_new), np.copy(σ_new)
+ return c_out, a_out, i, error
- if error > tol:
- print("未能收敛!")
- elif verbose:
- print(f"\n在第{i}次迭代中收敛。")
+ i, error = 0, tol + 1
+ initial_state = (c_init, a_init, i, error)
+ final_loop_state = jax.lax.while_loop(condition, body, initial_state)
+ c_out, a_out, i, error = final_loop_state
- return a_new, σ_new
+ return c_out, a_out
```
-现在我们可以用默认参数创建一个实例。
+现在我们可以创建一个实例并使用 JAX 求解模型:
-```{code-cell} ipython
-ifp = IFP()
+```{code-cell} ipython3
+ifp = create_ifp()
```
-接下来我们设置一个初始条件,对应“消费掉所有资产”。
+设置初始条件:
-```{code-cell} ipython
+```{code-cell} ipython3
# 初始猜测 σ = 消费所有资产
k = len(ifp.s_grid)
n = len(ifp.P)
-σ_init = np.empty((k, n))
+σ_init = jnp.empty((k, n))
for z in range(n):
- σ_init[:, z] = ifp.s_grid
-a_init = np.copy(σ_init)
+ σ_init = σ_init.at[:, z].set(ifp.s_grid)
+a_init = σ_init.copy()
```
-让我们生成一个近似解。
+让我们用 JAX 生成一个近似解:
-```{code-cell} ipython
-a_star, σ_star = solve_model_time_iter(ifp, a_init, σ_init, print_skip=5)
+```{code-cell} ipython3
+a_star, σ_star = solve_model(ifp, a_init, σ_init)
```
-这是结果消费政策的图:
+让我们再用计时器试一次。
-```{code-cell} ipython
-fig, ax = plt.subplots()
-for z in range(len(ifp.P)):
- ax.plot(a_star[:, z], σ_star[:, z], label=f"当 $z={z}$ 时的消费")
+```{code-cell} python3
+with qe.Timer(precision=8):
+ a_star, σ_star = solve_model(ifp, a_init, σ_init)
+ a_star.block_until_ready()
+```
-plt.legend()
-plt.show()
+## 模拟
+
+让我们回到默认模型,研究资产的平稳分布。
+
+我们的计划是让大量家庭向前推进 $T$ 期,然后绘制资产横截面分布的直方图。
+
+设置 `num_households=50_000, T=500`。
+
+首先我们编写一个函数,将单个家庭向前模拟,并记录资产的最终值。
+
+该函数接受一对解 `c_vec` 和 `a_vec`,将其理解为与给定模型 `ifp` 相关联的最优政策。
+
+```{code-cell} ipython3
+def simulate_household(
+ key, a_0, z_idx_0, c_vec, a_vec, ifp, T
+ ):
+ """
+ 模拟单个家庭 T 期,以逼近资产的平稳分布。
+
+ - key 是随机数生成器的状态
+ - ifp 是 IFP 的一个实例
+ - c_vec, a_vec 是 ifp 的最优消费政策和内生网格
+
+ """
+ # 从 ifp 中提取参数
+ γ, β, P, a_r, b_r, a_y, b_y, s_grid, η_draws, ζ_draws = ifp
+ n_z = len(P)
+
+ # 为消费政策创建插值函数
+ σ = lambda a, z_idx: jnp.interp(a, a_vec[:, z_idx], c_vec[:, z_idx])
+
+ # 向前模拟 T 期
+ def update(t, state):
+ a, z_idx = state
+ # 从 P[z, z'] 中抽取下一期冲击 z'
+ current_key = jax.random.fold_in(key, 3*t)
+ z_next_idx = jax.random.choice(current_key, n_z, p=P[z_idx]).astype(jnp.int32)
+ # 为收入抽取 η 冲击
+ η_key = jax.random.fold_in(key, 3*t + 1)
+ η = jax.random.normal(η_key)
+ # 为收益率抽取 ζ 冲击
+ ζ_key = jax.random.fold_in(key, 3*t + 2)
+ ζ = jax.random.normal(ζ_key)
+ # 计算随机收益率
+ R_next = R(z_next_idx, ζ, a_r, b_r)
+ # 计算收入
+ Y_next = Y(z_next_idx, η, a_y, b_y)
+ # 更新资产:a' = R' * (a - c) + Y'
+ a_next = R_next * (a - σ(a, z_idx)) + Y_next
+ # 返回更新后的状态
+ return a_next, z_next_idx
+
+ initial_state = a_0, z_idx_0
+ final_state = jax.lax.fori_loop(0, T, update, initial_state)
+ a_final, _ = final_state
+ return a_final
```
-注意,在资产空间的较低区间,我们会消费掉所有资产。
+现在我们编写一个函数,并行模拟许多家庭。
-这是因为我们预期下一期会有收入 $Y_{t+1}$,因此储蓄的紧迫性较低。
+```{code-cell} ipython3
+@partial(jax.jit, static_argnums=(3, 4, 5))
+def compute_asset_stationary(
+ c_vec, a_vec, ifp, num_households=50_000, T=500, seed=1234
+ ):
+ """
+ 模拟 num_households 个家庭 T 期,以逼近资产的平稳分布。
-你能解释为什么在 $z=0$ 时,消费掉所有资产会更早结束(即在较低的资产水平就停止)吗?
+ 返回资产持有量的最终横截面。
-### 运动规律
+ - ifp 是 IFP 的一个实例
+ - c_vec, a_vec 是最优消费政策和内生网格。
-让我们试着了解,在这种消费政策下,从长期来看资产会如何变化。
+ """
+ # 从 ifp 中提取参数
+ γ, β, P, a_r, b_r, a_y, b_y, s_grid, η_draws, ζ_draws = ifp
+
+ # 从 资产 = 储蓄网格最大值 / 2 开始
+ a_0_vector = jnp.full(num_households, s_grid[-1] / 2)
+ # 初始化每个家庭的外生状态
+ z_idx_0_vector = jnp.zeros(num_households).astype(jnp.int32)
+
+ # 对许多家庭进行向量化
+ key = jax.random.PRNGKey(seed)
+ keys = jax.random.split(key, num_households)
+ # 在 (key, a_0, z_idx_0) 上向量化 simulate_household
+ sim_all_households = jax.vmap(
+ simulate_household, in_axes=(0, 0, 0, None, None, None, None)
+ )
+ assets = sim_all_households(keys, a_0_vector, z_idx_0_vector, c_vec, a_vec, ifp, T)
+
+ return jnp.array(assets)
+```
-与我们在 {doc}`之前关于收入波动问题的讲座` 中一样,我们首先制作一个 45 度图,展示资产的运动规律:
+我们需要一些不平等度量来进行可视化,所以让我们先定义它们:
-```{code-cell} python3
-# 好状态和坏状态的平均劳动收入
-Y_mean = [np.mean(ifp.Y(z, ifp.η_draws)) for z in (0, 1)]
-# 平均收益
-R_mean = np.mean(ifp.R(z, ifp.ζ_draws))
+```{code-cell} ipython3
+def gini_coefficient(x):
+ """
+ 计算数组 x 的基尼系数。
-a = a_star
-fig, ax = plt.subplots()
-for z, lb in zip((0, 1), ('坏状态', '好状态')):
- ax.plot(a[:, z], R_mean * (a[:, z] - σ_star[:, z]) + Y_mean[z] , label=lb)
+ """
+ x = jnp.asarray(x)
+ n = len(x)
+ x_sorted = jnp.sort(x)
+ # 计算基尼系数
+ cumsum = jnp.cumsum(x_sorted)
+ a = (2 * jnp.sum((jnp.arange(1, n+1)) * x_sorted)) / (n * cumsum[-1])
+ return a - (n + 1) / n
+
+
+def top_share(
+ x: jnp.array, # 财富值数组
+ p: float=0.01 # 头部家庭的比例(默认 0.01 表示前 1%)
+ ):
+ """
+ 计算前 p 比例家庭所持有的总财富份额。
-ax.plot(a[:, 0], a[:, 0], 'k--')
-ax.set(xlabel='当前资产', ylabel='下一期资产')
+ """
+ x = jnp.asarray(x)
+ x_sorted = jnp.sort(x)
+ # 前 p% 中的家庭数量
+ n_top = int(jnp.ceil(len(x) * p))
+ # 前 p% 持有的财富
+ wealth_top = jnp.sum(x_sorted[-n_top:])
+ # 总财富
+ wealth_total = jnp.sum(x_sorted)
+ return wealth_top / wealth_total
+```
-ax.legend()
+现在我们调用该函数,生成资产分布并将其可视化:
+
+```{code-cell} ipython3
+ifp = create_ifp()
+# 提取用于初始化的参数
+s_grid = ifp.s_grid
+n_z = len(ifp.P)
+a_init = s_grid[:, None] * jnp.ones(n_z)
+c_init = a_init
+a_vec, c_vec = solve_model(ifp, a_init, c_init)
+assets = compute_asset_stationary(c_vec, a_vec, ifp, num_households=200_000)
+
+# 为图形计算基尼系数
+gini_plot = gini_coefficient(assets)
+
+# 绘制对数财富直方图
+fig, ax = plt.subplots(figsize=(10, 6))
+ax.hist(jnp.log(assets), bins=40, alpha=0.5, density=True)
+ax.set(xlabel='对数资产', ylabel='密度', title="财富分布")
+plt.tight_layout()
plt.show()
```
-图中实线表示对于每个 $z$,资产的平均更新函数,由下式给出:
+直方图显示了对数财富的分布。
-$$
-a \mapsto \bar R (a - \sigma^*(a, z)) + \bar Y(z)
-$$
+请记住我们看的是对数值,直方图表明分布有较长的右尾。
-其中
+下面我们更详细地研究这一点。
-* $\bar R = \mathbb E R_t$,即平均收益率,且
-* $\bar Y(z) = \mathbb E_z Y(z, \eta_t)$,即状态 $z$ 下的平均劳动收入。
-虚线是 45 度线。
-从图中可以看出,动态是稳定的——即使在最高的状态下,资产也不会发散。
+## 财富不平等
-## 练习
+让我们通过计算这一现象的一些标准度量来考察财富不平等。
-```{exercise}
-:label: ifpa_ex1
+我们还将考察不平等程度如何随利率变化。
+
+
+### 度量不平等
+
+让我们打印出模拟结果中的基尼系数和前 1% 财富份额:
+
+```{code-cell} ipython3
+gini = gini_coefficient(assets)
+top1 = top_share(assets, p=0.01)
+
+print(f"基尼系数:{gini:.4f}")
+print(f"前 1% 财富份额:{top1:.4f}")
+```
+
+最近的数据表明
+
+* 美国财富的基尼系数约为 0.8
+* 前 1% 的财富份额超过 0.3
-让我们重复 {ref}`之前的练习 `,研究资产的长期横截面分布。
+我们具有随机收益的模型生成的基尼系数接近经验值,这表明资本收入风险是财富不平等的一个重要因素。
-在那个练习中,我们使用了一个相对简单的收入波动模型。
+然而,前 1% 的财富份额过大。
-在解答中,我们发现资产分布的形状不切实际。
+我们的模型需要适当的校准和进一步的工作——我们暂时搁置这些任务。
-特别是,我们未能匹配财富分布的长右尾。
+## 练习
+
+```{exercise}
+:label: ifp_advanced_ex1
-你的任务是再次尝试这个练习,但这一次使用我们更复杂的模型。
+绘制基尼系数如何随资产收益的波动性变化。
+
+具体而言,计算 `a_r` 从 0.10 到 0.16 变化时的基尼系数(至少使用 5 个不同的值),并绘制结果图。
+
+这告诉我们资本收入风险与财富不平等之间的关系是什么?
-使用默认参数。
```
-```{solution-start} ifpa_ex1
+```{solution-start} ifp_advanced_ex1
:class: dropdown
```
-首先我们编写一个函数来生成一个较长的资产序列。
+我们对不同的 `a_r` 值进行循环,为每个值求解模型,模拟财富分布,并计算基尼系数。
+
+```{code-cell} ipython3
+# 需要探索的 a_r 值范围
+a_r_vals = np.linspace(0.10, 0.16, 5)
+gini_vals = []
+
+print("正在计算不同收益波动性下的基尼系数...\n")
+
+for a_r in a_r_vals:
+ print(f"a_r = {a_r:.3f}...", end=" ")
+
+ # 用这个 a_r 值创建模型
+ ifp_temp = create_ifp(a_r=a_r, grid_max=100)
+
+ # 求解模型
+ s_grid_temp = ifp_temp.s_grid
+ n_z_temp = len(ifp_temp.P)
+ a_init_temp = s_grid_temp[:, None] * jnp.ones(n_z_temp)
+ c_init_temp = a_init_temp
+ a_vec_temp, c_vec_temp = solve_model(
+ ifp_temp, a_init_temp, c_init_temp
+ )
+
+ # 模拟家庭
+ assets_temp = compute_asset_stationary(
+ c_vec_temp, a_vec_temp, ifp_temp, num_households=200_000
+ )
+
+ # 计算基尼系数
+ gini_temp = gini_coefficient(assets_temp)
+ gini_vals.append(gini_temp)
+ print(f"基尼系数 = {gini_temp:.4f}")
+
+# 绘制结果图
+fig, ax = plt.subplots(figsize=(10, 6))
+ax.plot(a_r_vals, gini_vals, 'o-', linewidth=2, markersize=8)
+ax.set(xlabel='收益波动性 (a_r)',
+ ylabel='基尼系数',
+ title='财富不平等与收益波动性')
+ax.axhline(y=0.8, color='k', linestyle='--', linewidth=1,
+ label='美国经验基尼系数 (~0.8)')
+ax.legend()
+plt.tight_layout()
+plt.show()
+```
-因为我们希望用 JIT 来编译函数,所以在写代码时不得不打破一些良好的编程风格。
+图中显示,财富不平等(用基尼系数衡量)随收益波动性的增大而增加。
-例如,我们会把解 `a_star, σ_star` 以及 `ifp` 一起传入,尽管更自然的方式是只传入 `ifp` 然后在函数内求解。
+这表明资本收入风险是财富不平等的一个关键驱动因素。
-我们这样做的原因是 `solve_model_time_iter` 不是 JIT 编译的。
+当收益波动性更大时,经历了一系列高收益的幸运家庭会积累远多于不幸家庭的财富,从而导致财富分布的不平等程度加剧。
-```{code-cell} python3
-@jit
-def compute_asset_series(ifp, a_star, σ_star, z_seq, T=500_000):
- """
- 在最优储蓄行为下,模拟长度为 T 的资产时间序列
- * ifp 是 IFP 的实例
- * a_star 是内生网格解
- * σ_star 是网格上的最优消费
- * z_seq 是 {Z_t} 的时间路径
+```{solution-end}
+```
- """
+```{exercise}
+:label: ifp_advanced_ex2
- # 通过线性插值创建消费函数
- σ = lambda a, z: np.interp(a, a_star[:, z], σ_star[:, z])
+绘制基尼系数如何随劳动收入的波动性变化。
- # 模拟资产路径
- a = np.zeros(T+1)
- for t in range(T):
- z = z_seq[t]
- ζ, η = np.random.randn(), np.random.randn()
- R = ifp.R(z, ζ)
- Y = ifp.Y(z, η)
- a[t+1] = R * (a[t] - σ(a[t], z)) + Y
- return a
-```
+具体而言,计算 `a_y` 从 0.125 到 0.20 变化时的基尼系数,并绘制结果图。在本练习中设置 `a_r=0.10`。
-接下来,我们调用该函数,生成资产序列,并利用上面的解来绘制直方图。
+这告诉我们劳动收入风险与财富不平等之间的关系是什么?通过改变劳动收入波动性,我们能否达到与改变收益波动性同样程度的不平等上升?
-```{code-cell} python3
-T = 1_000_000
-mc = MarkovChain(ifp.P)
-z_seq = mc.simulate(T, random_state=1234)
+```
-a = compute_asset_series(ifp, a_star, σ_star, z_seq, T=T)
+```{solution-start} ifp_advanced_ex2
+:class: dropdown
+```
-fig, ax = plt.subplots()
-ax.hist(a, bins=40, alpha=0.5, density=True)
-ax.set(xlabel='资产')
+我们对不同的 `a_y` 值进行循环,为每个值求解模型,模拟财富分布,并计算基尼系数。
+
+```{code-cell} ipython3
+# 需要探索的 a_y 值范围
+a_y_vals = np.linspace(0.125, 0.20, 5)
+gini_vals_y = []
+
+print("正在计算不同劳动收入波动性下的基尼系数...\n")
+
+for a_y in a_y_vals:
+ print(f"a_y = {a_y:.3f}...", end=" ")
+
+ # 用这个 a_y 值和 a_r=0.10 创建模型
+ ifp_temp = create_ifp(a_y=a_y, a_r=0.10, grid_max=100)
+
+ # 求解模型
+ s_grid_temp = ifp_temp.s_grid
+ n_z_temp = len(ifp_temp.P)
+ a_init_temp = s_grid_temp[:, None] * jnp.ones(n_z_temp)
+ c_init_temp = a_init_temp
+ a_vec_temp, c_vec_temp = solve_model(
+ ifp_temp, a_init_temp, c_init_temp
+ )
+
+ # 模拟家庭
+ assets_temp = compute_asset_stationary(
+ c_vec_temp, a_vec_temp, ifp_temp, num_households=200_000
+ )
+
+ # 计算基尼系数
+ gini_temp = gini_coefficient(assets_temp)
+ gini_vals_y.append(gini_temp)
+ print(f"基尼系数 = {gini_temp:.4f}")
+
+# 绘制结果图
+fig, ax = plt.subplots(figsize=(10, 6))
+ax.plot(a_y_vals, gini_vals_y, 'o-', linewidth=2, markersize=8, color='green')
+ax.set(xlabel='劳动收入波动性 (a_y)',
+ ylabel='基尼系数',
+ title='财富不平等与劳动收入波动性')
+ax.axhline(y=0.8, color='k', linestyle='--', linewidth=1,
+ label='美国经验基尼系数 (~0.8)')
+ax.legend()
+plt.tight_layout()
plt.show()
```
-我们现在已经成功再现了财富分布的长右尾。
+图中显示,财富不平等随劳动收入波动性的增大而增加,但这一效应比收益波动性的效应要弱得多。
-下面用一张水平小提琴图来展示这一结果的另一种视角。
+比较这两个练习:
+
+- 当收益波动性(`a_r`)从 0.10 变化到 0.16 时,基尼系数从约 0.20 急剧上升到 0.79
+- 当劳动收入波动性(`a_y`)以类似的百分比幅度从 0.125 变化到 0.20 时,基尼系数虽有增加,但增幅小得多
+
+这表明,相较于劳动收入风险,资本收入风险是财富不平等更重要的驱动因素。
+
+其直觉在于,财富积累会随时间产生复利效应:经历了有利资产收益的家庭可以将这些收益进行再投资,从而实现指数级增长。
+
+相比之下,劳动收入冲击虽然会影响当期消费和储蓄,但对财富积累不具有同样的复利效应。
-```{code-cell} python3
-fig, ax = plt.subplots()
-ax.violinplot(a, vert=False, showmedians=True)
-ax.set(xlabel='资产')
-plt.show()
-```
```{solution-end}
```