diff --git a/lectures/_toc.yml b/lectures/_toc.yml index 694a32ba..0e950b57 100644 --- a/lectures/_toc.yml +++ b/lectures/_toc.yml @@ -66,7 +66,7 @@ parts: - file: mccall_model - file: mccall_model_with_separation - file: mccall_fitted_vfi - - file: mccall_correlated + - file: mccall_persist_trans - file: career - file: jv - file: mccall_q @@ -79,13 +79,10 @@ parts: - file: cass_fiscal - file: cass_fiscal_2 - file: ak2 - - file: cake_eating_problem - - file: cake_eating_numerical - - file: optgrowth - - file: optgrowth_fast - - file: coleman_policy_iter - - file: egm_policy_iter - - file: ifp + - file: os + - file: os_numerical + - file: os_stochastic + - file: ifp_egm - file: ifp_advanced - caption: LQ控制 numbered: true diff --git a/lectures/coleman_policy_iter.md b/lectures/coleman_policy_iter.md deleted file mode 100644 index 337c1e9f..00000000 --- a/lectures/coleman_policy_iter.md +++ /dev/null @@ -1,426 +0,0 @@ ---- -jupytext: - text_representation: - extension: .md - format_name: myst -kernelspec: - display_name: Python 3 - language: python - name: python3 ---- - -```{raw} jupyter -
- - QuantEcon - -
-``` - -# {index}`最优增长 III:时间迭代 ` - -```{contents} 目录 -:depth: 2 -``` - -除Anaconda已包含的库外,本讲义还需要安装以下库: - -```{code-cell} ipython ---- -tags: [hide-output] ---- -!pip install quantecon -``` -## 概述 - -在本讲中,我们将继续此前对{doc}`随机最优增长模型 `的研究。 - -在那一讲中,我们使用价值函数迭代求解了相关的动态规划问题。 - -这种技术的优点在于其广泛的适用性。 - -然而,在数值问题中,我们常常可以通过推导出更贴合具体应用的算法,以获得更高的效率。 - -随机最优增长模型具备丰富的结构可供利用,尤其当我们对原始要素施加某些凹性与光滑性假设时。 - -我们将利用这一结构,获得基于欧拉方程的方法。 - -这将是我们在基础讲义{doc}`吃蛋糕问题 `中所考虑的时间迭代方法的扩展。 - -在{doc}`下一讲 `中,我们将看到,时间迭代可以进一步调整,以获得更高的效率。 - -接下来,让我们从导入开始: - -```{code-cell} ipython -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 quantecon.optimize import brentq -from numba import jit -``` -## 欧拉方程 - -我们的第一步是推导欧拉方程,这是此前在{doc}`吃蛋糕问题 `中得到的欧拉方程的推广。 - -我们采用{doc}`随机增长模型 `中的模型设定,并加入以下假设: - -1. $u$ 和 $f$ 是连续可微且严格凹函数; -1. $f(0) = 0$; -1. $\lim_{c \to 0} u'(c) = \infty$ 且 $\lim_{c \to \infty} u'(c) = 0$; -1. $\lim_{k \to 0} f'(k) = \infty$ 且 $\lim_{k \to \infty} f'(k) = 0$。 - -最后两个条件通常被称为**Inada条件**。 - -回顾贝尔曼方程: - -```{math} -:label: cpi_fpb30 - -v^*(y) = \max_{0 \leq c \leq y} - \left\{ - u(c) + \beta \int v^*(f(y - c) z) \phi(dz) - \right\} -\quad \forall y \in \mathbb R_+ -``` - -令最优消费策略记为 $\sigma^*$。 - -我们知道 $\sigma^*$ 是一个 $v^*$-逐期最优的,因此 $\sigma^*(y)$ 是{eq}`cpi_fpb30`中的最大化解。 - -上述条件表明: - -* $\sigma^*$ 是随机最优增长模型的唯一最优策略; -* 该最优策略是连续的、严格递增的,并且是**内部解**,即对于所有严格正的 $y$,都有 $0 < \sigma^*(y) < y$; -* 价值函数是严格凹的且连续可微的,并满足: - -```{math} -:label: cpi_env - -(v^*)'(y) = u' (\sigma^*(y) ) := (u' \circ \sigma^*)(y) -``` - -最后一个结果被称为**包络条件**,因为它与[包络定理](https://baike.baidu.com/item/%E5%8C%85%E7%BB%9C%E5%AE%9A%E7%90%86/5746200)有关。 - -要理解为什么{eq}`cpi_env`成立,可以将贝尔曼方程写成等价形式: - -$$ -v^*(y) = \max_{0 \leq k \leq y} - \left\{ - u(y-k) + \beta \int v^*(f(k) z) \phi(dz) - \right\}, -$$ - -对 $y$ 求导,并在最优解处求值,即可得到{eq}`cpi_env`。 - -([EDTC](https://johnstachurski.net/edtc.html)第12.1节给出了这些结果的完整证明,许多其他教材中也可找到类似讨论。) - -价值函数的可微性和最优策略的内部性意味着,最优消费决策满足与{eq}`cpi_fpb30`相关的一阶条件,即 - -```{math} -:label: cpi_foc - -u'(\sigma^*(y)) = \beta \int (v^*)'(f(y - \sigma^*(y)) z) f'(y - \sigma^*(y)) z \phi(dz) -``` - -将{eq}`cpi_env`和该一阶条件{eq}`cpi_foc`结合,得到**欧拉方程**: - -```{math} -:label: cpi_euler - -(u'\circ \sigma^*)(y) -= \beta \int (u'\circ \sigma^*)(f(y - \sigma^*(y)) z) f'(y - \sigma^*(y)) z \phi(dz) -``` - -我们可以将欧拉方程视为一个函数方程: - -```{math} -:label: cpi_euler_func - -(u'\circ \sigma)(y) -= \beta \int (u'\circ \sigma)(f(y - \sigma(y)) z) f'(y - \sigma(y)) z \phi(dz) -``` -其中 $\sigma$ 为内部消费策略,其解之一即为最优策略 $\sigma^*$。 - -我们的目标是求解函数方程 {eq}`cpi_euler_func` 从而获得 $\sigma^*$。 - -### Coleman-Reffett 算子 - -回顾贝尔曼算子: - -```{math} -:label: fcbell20_coleman - -Tv(y) := \max_{0 \leq c \leq y} -\left\{ - u(c) + \beta \int v(f(y - c) z) \phi(dz) -\right\} -``` - -正如我们引入贝尔曼算子来求解贝尔曼方程一样,我们现在将引入一个作用于策略空间的算子,用于帮助我们求解欧拉方程。 - -该算子 $K$ 将作用于所有连续、严格递增且为内部解的 $\sigma \in \Sigma$。 - -此后我们将这类策略集合记为 $\mathscr P$。 - -1. 算子 $K$ 的自变量是一个 $\sigma \in \mathscr P$; -1. 返回一个新函数 $K\sigma$,其中 $(K\sigma)(y)$ 是求解以下方程的 $c \in (0, y)$: - -```{math} -:label: cpi_coledef - -u'(c) -= \beta \int (u' \circ \sigma) (f(y - c) z ) f'(y - c) z \phi(dz) -``` - -我们称这个算子为**Coleman-Reffett算子**,以此致敬{cite}`Coleman1990`和{cite}`Reffett1996`的研究工作。 - -本质上,$K\sigma$ 表示在给定未来消费策略为 $\sigma$ 时,欧拉方程指导你今天应选择的消费策略。 - -值得注意的是:依据构造,算子 $K$ 的不动点恰好与函数方程{eq}`cpi_euler_func`的解相一致。 - -特别地,最优政策 $\sigma^*$ 是一个不动点。 - -事实上,对于固定的 $y$,$(K\sigma^*)(y)$ 是满足以下方程的 $c$: - -$$ -u'(c) -= \beta \int (u' \circ \sigma^*) (f(y - c) z ) f'(y - c) z \phi(dz) -$$ - -根据欧拉方程,该解正是 $\sigma^*(y)$。 - -### Coleman-Reffett算子是否定义良好? - -特别地,是否总存在唯一的 $c \in (0, y)$ 使其满足{eq}`cpi_coledef`? - -在我们的假设条件下,答案是肯定的。 - -对于任何 $\sigma \in \mathscr P$,{eq}`cpi_coledef` 的右侧: - -* 在 $(0, y)$ 上关于 $c$ 是连续且严格递增的; -* 当 $c \uparrow y$ 时趋向于 $+\infty$。 - -{eq}`cpi_coledef` 的左侧: - -* 在 $(0, y)$ 上关于 $c$ 是连续且严格递减的; -* 当 $c \downarrow 0$ 时趋向于 $+\infty$。 - -绘制这些曲线并利用上述信息,二者在 $c \in (0, y)$ 上恰好有且仅有一次交点。 - -进一步分析可得:若 $\sigma \in \mathscr P$,则 $K \sigma \in \mathscr P$。 - -### 与价值函数迭代的理论比较 - -可以证明,算子 $K$ 的迭代与贝尔曼算子的迭代之间存在紧密关系。 - -从数学上讲,这两个算子是*拓扑共轭的*。 - -简单来说,这意味着:如果一个算子的迭代收敛,那么另一个算子的迭代也会收敛,反之亦然。 - -此外,在理论上可以认为二者的收敛速率是相同的。 - -然而,事实证明,算子 $K$ 在数值计算上更加稳定,因此在我们考虑的应用中更加高效。 - -下面给出若干示例。 - -## 实现 - -与{doc}`上一讲 `一样,我们继续假设: - -* $u(c) = \ln c$; -* $f(k) = k^{\alpha}$; -* $\phi$ 是 $\xi := \exp(\mu + s \zeta)$ 的分布,且 $\zeta$ 服从标准正态分布。 - -这一设定使我们能够将数值结果与解析解进行比较。 - -```{code-cell} ipython3 -:load: _static/lecture_specific/optgrowth/cd_analytical.py -``` -如上所述,我们的目标是通过时间迭代来求解模型,即对算子 $K$ 进行迭代。 - -为此,我们需要函数 $u', f$ 和 $f'$。 - -我们将使用{doc}`上一讲 `中构建的`OptimalGrowthModel`类来实现。 - -```{code-cell} ipython3 -:load: _static/lecture_specific/optgrowth_fast/ogm.py -``` -接下来我们实现一个名为`euler_diff`的方法,该方法返回: - -```{math} -:label: euler_diff - -u'(c) - \beta \int (u' \circ \sigma) (f(y - c) z ) f'(y - c) z \phi(dz) -``` - -```{code-cell} ipython -@jit -def euler_diff(c, σ, y, og): - """ - 设置一个函数,使得关于c的根, - 在给定y和σ的情况下,等于Kσ(y)。 - - """ - - β, shocks, grid = og.β, og.shocks, og.grid - f, f_prime, u_prime = og.f, og.f_prime, og.u_prime - - # 首先通过插值将σ转换为函数 - σ_func = lambda x: np.interp(x, grid, σ) - - # 现在设置我们需要找到根的函数 - vals = u_prime(σ_func(f(y - c) * shocks)) * f_prime(y - c) * shocks - return u_prime(c) - β * np.mean(vals) -``` -函数`euler_diff`通过蒙特卡洛方法计算积分,并使用线性插值对函数进行近似。 - -我们将使用求根算法来求解式{eq}`euler_diff`,给定状态 $y$ 和 $σ$,寻找当前期消费 $c$。 - -下面是实现该求根算法的算子 $K$。 - -```{code-cell} ipython3 -@jit -def K(σ, og): - """ - Coleman-Reffett算子 - - 这里og是OptimalGrowthModel的一个实例。 - """ - - β = og.β - f, f_prime, u_prime = og.f, og.f_prime, og.u_prime - grid, shocks = og.grid, og.shocks - - σ_new = np.empty_like(σ) - for i, y in enumerate(grid): - # 在y处求解最优c - c_star = brentq(euler_diff, 1e-10, y-1e-10, args=(σ, y, og))[0] - σ_new[i] = c_star - - return σ_new -``` -### 测试 - -接下来,我们生成一个实例并绘制算子 $K$ 的若干次迭代结果,初始条件取 $σ(y) = y$。 - -```{code-cell} ipython3 -og = OptimalGrowthModel() -grid = og.grid - -n = 15 -σ = grid.copy() # 设置初始条件 - -fig, ax = plt.subplots() -lb = '初始条件 $\sigma(y) = y$' -ax.plot(grid, σ, color=plt.cm.jet(0), alpha=0.6, label=lb) - -for i in range(n): - σ = K(σ, og) - ax.plot(grid, σ, color=plt.cm.jet(i / n), alpha=0.6) - -# 再更新一次并用黑色绘制最后一次迭代 -σ = K(σ, og) -ax.plot(grid, σ, color='k', alpha=0.8, label='最后一次迭代') - -ax.legend() - -plt.show() -``` -我们可以看到,迭代过程快速收敛到一个极限,该极限与我们在{doc}`上一讲`中得到的解非常相似。 - -这里给出一个名为`solve_model_time_iter`的函数,它接收一个`OptimalGrowthModel`实例作为输入,并通过时间迭代法返回最优策略的近似解。 - -```{code-cell} ipython3 -:load: _static/lecture_specific/coleman_policy_iter/solve_time_iter.py -``` -让我们运行它: - -```{code-cell} ipython3 -σ_init = np.copy(og.grid) -σ = solve_model_time_iter(og, σ_init) -``` -这是得到的策略与真实策略的对比图: - -```{code-cell} ipython3 -fig, ax = plt.subplots() - -ax.plot(og.grid, σ, lw=2, - alpha=0.8, label='近似策略函数') - -ax.plot(og.grid, σ_star(og.grid, og.α, og.β), 'k--', - lw=2, alpha=0.8, label='真实策略函数') - -ax.legend() -plt.show() -``` -再次说明,拟合效果非常好。 - -两种策略之间的最大绝对偏差是: - -```{code-cell} ipython3 -np.max(np.abs(σ - σ_star(og.grid, og.α, og.β))) -``` -收敛所需时间如下: - -```{code-cell} ipython3 -%%timeit -n 3 -r 1 -σ = solve_model_time_iter(og, σ_init, verbose=False) -``` -收敛速度非常快,甚至优于我们{doc}`基于JIT编译的价值函数迭代`。 - -总的来说,我们发现,至少对于该模型而言,时间迭代法在效率与准确度上均展现出高度优势。 - -## 练习 - -```{exercise} -:label: cpi_ex1 - -求解具有CRRA效用函数的模型 - -$$ -u(c) = \frac{c^{1 - \gamma}} {1 - \gamma} -$$ - -其中`γ = 1.5`。 - -计算并绘制最优策略。 -``` - -```{solution-start} cpi_ex1 -:class: dropdown -``` - -我们使用{doc}`VFI讲义`中的`OptimalGrowthModel_CRRA`类。 - -```{code-cell} ipython3 -:load: _static/lecture_specific/optgrowth_fast/ogm_crra.py -``` - -创建一个实例: - -```{code-cell} ipython3 -og_crra = OptimalGrowthModel_CRRA() -``` - -求解并绘制策略: - -```{code-cell} ipython3 -%%time -σ = solve_model_time_iter(og_crra, σ_init) - - -fig, ax = plt.subplots() - -ax.plot(og.grid, σ, lw=2, - alpha=0.8, label='近似策略函数') - -ax.legend() -plt.show() -``` - -```{solution-end} -``` diff --git a/lectures/egm_policy_iter.md b/lectures/egm_policy_iter.md deleted file mode 100644 index 4c02fb97..00000000 --- a/lectures/egm_policy_iter.md +++ /dev/null @@ -1,251 +0,0 @@ ---- -jupytext: - text_representation: - extension: .md - format_name: myst -kernelspec: - display_name: Python 3 - language: python - name: python3 ---- - -```{raw} jupyter - -``` - -# {index}`最优增长 IV:内生网格法 ` - -```{contents} 目录 -:depth: 2 -``` - -## 概述 - -在之前,我们使用以下方法求解了随机最优增长模型: - -1. {doc}`价值函数迭代 ` -1. {doc}`基于欧拉方程的时间迭代 ` - -我们发现时间迭代在准确性和效率方面都明显更好。 - -在本讲义中,我们将介绍时间迭代的一种巧妙变体,称为**内生网格法**(EGM)。 - -EGM是由[Chris Carroll](http://www.econ2.jhu.edu/people/ccarroll/)发明的一种用于实现政策迭代的数值方法。 - -该方法的原始参考文献是{cite}`Carroll2006`。 - -让我们从一些标准导入开始: - -```{code-cell} ipython -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 -``` - -## 核心思想 - -我们首先回顾理论背景,然后说明数值方法如何融入其中。 - -### 理论 - -我们沿用{doc}`时间迭代 `中的模型设定,遵循相同的术语和符号。 - -欧拉方程为: - -```{math} -:label: egm_euler - -(u'\circ \sigma^*)(y) -= \beta \int (u'\circ \sigma^*)(f(y - \sigma^*(y)) z) f'(y - \sigma^*(y)) z \phi(dz) -``` - -如前所示,Coleman-Reffett算子是一个非线性算子 $K$,其设计使得 $\sigma^*$ 是 $K$ 的不动点。 - -该算子以一个连续且严格递增的消费策略 $\sigma \in \Sigma$ 作为自变量。 - -它返回一个新函数 $K \sigma$,其中 $(K \sigma)(y)$ 是满足以下方程的 $c \in (0, \infty)$: - -```{math} -:label: egm_coledef - -u'(c) -= \beta \int (u' \circ \sigma) (f(y - c) z ) f'(y - c) z \phi(dz) -``` - -### 外生网格 - -如{doc}`时间迭代 `中所述,为了在计算机上实现该方法,我们需要数值近似。 - -具体来说,我们通过在有限网格上取值的方式表示策略函数。 - -在需要时,使用插值或其他方法从该有限表示中重建原函数。 - -在{doc}`时间迭代 `中,为了获得更新后的消费策略的有限表示,我们: - -* 固定一组收入点 $\{y_i\}$; -* 使用{eq}`egm_coledef`与求根算法,计算与每个 $y_i$ 对应的消费值 $c_i$。 - -每个 $c_i$ 被解释为函数 $K \sigma$ 在 $y_i$ 处的值。 - -因此,有了点集 $\{y_i, c_i\}$,我们可以通过近似重建 $K \sigma$。 - -然后继续迭代... - -### 内生网格 - -上述方法需要通过求根算法来确定与给定收入值 $y_i$ 对应的消费水平 $c_i$。 - -然而,求根运算的代价较高,因为其通常涉及大量函数求值。 - -正如Carroll {cite}`Carroll2006`所指出的,如果 $y_i$ 是内生选择的,则可避免这一过程。 - -唯一需要的假设是:$u'$ 在 $(0, \infty)$ 上是可逆的。 - -令 $(u')^{-1}$ 为 $u'$ 的反函数。 - -其核心思想如下: - -* 首先,固定一个关于资本($k = y - c$)的*外生*网格 $\{k_i\}$; -* 接着,根据以下公式求得 $c_i$: - -```{math} -:label: egm_getc - -c_i = -(u')^{-1} -\left\{ - \beta \int (u' \circ \sigma) (f(k_i) z ) \, f'(k_i) \, z \, \phi(dz) -\right\} -``` - -* 最后,对每个 $c_i$,设定 $y_i = c_i + k_i$。 - -显然,每个通过上述方式构建的 $(y_i, c_i)$ 都满足{eq}`egm_coledef`。 - -有了点集 $\{y_i, c_i\}$,我们即可通过近似方法重建 $K \sigma$。 - -新的EGM算法的关键在于:网格 $\{y_i\}$ 是**内生**决定的。 - -## 实现 - -与{doc}`时间迭代 `相同,我们从一个简单设定开始: - -* $u(c) = \ln c$; -* 生产函数是柯布-道格拉斯形式; -* 冲击项服从对数正态分布。 - -这一设定使我们能够将数值解与解析解进行对比。 - -```{code-cell} ipython3 -:load: _static/lecture_specific/optgrowth/cd_analytical.py -``` - -我们重用 `OptimalGrowthModel` 类 - -```{code-cell} ipython3 -:load: _static/lecture_specific/optgrowth_fast/ogm.py -``` - -### 算子 - -以下给出使用EGM方法实现算子 $K$ 的代码: - -```{code-cell} ipython3 -@jit -def K(σ_array, og): - """ - 使用EGM的Coleman-Reffett算子 - - """ - - # 简化命名 - f, β = og.f, og.β - f_prime, u_prime = og.f_prime, og.u_prime - u_prime_inv = og.u_prime_inv - grid, shocks = og.grid, og.shocks - - # 确定内生网格 - y = grid + σ_array # y_i = k_i + c_i - - # 使用内生网格进行策略的线性插值 - σ = lambda x: np.interp(x, y, σ_array) - - # 为新的消费数组分配内存 - c = np.empty_like(grid) - - # 求解更新后的消费值 - for i, k in enumerate(grid): - vals = u_prime(σ(f(k) * shocks)) * f_prime(k) * shocks - c[i] = u_prime_inv(β * np.mean(vals)) - - return c -``` - -值得注意的是,该算法不需要求根算法。 - -### 测试 - -首先,我们创建一个实例。 - -```{code-cell} ipython3 -og = OptimalGrowthModel() -grid = og.grid -``` - -下面是求解程序: - -```{code-cell} ipython3 -:load: _static/lecture_specific/coleman_policy_iter/solve_time_iter.py -``` - -让我们运行它: - -```{code-cell} ipython3 -σ_init = np.copy(grid) -σ = solve_model_time_iter(og, σ_init) -``` - -以下是得到的策略与真实策略的比较: - -```{code-cell} ipython3 -y = grid + σ # y_i = k_i + c_i - -fig, ax = plt.subplots() - -ax.plot(y, σ, lw=2, - alpha=0.8, label='近似策略函数') - -ax.plot(y, σ_star(y, og.α, og.β), 'k--', - lw=2, alpha=0.8, label='真实策略函数') - -ax.legend() -plt.show() -``` - -两个策略之间的最大绝对偏差是 - -```{code-cell} ipython3 -np.max(np.abs(σ - σ_star(y, og.α, og.β))) -``` - -收敛所需的时间为: - -```{code-cell} ipython3 -%%timeit -n 3 -r 1 -σ = solve_model_time_iter(og, σ_init, verbose=False) -``` - -相较于已被证明高度高效的时间迭代法,内生网格法(EGM)在保持精度不变的前提下,进一步显著减少了运行时间。 - -其主要原因在于该方法不需要进行数值求根步骤。 - -因此,我们能够在给定参数下以极高的速度求解最优增长模型。 \ No newline at end of file diff --git a/lectures/ifp.md b/lectures/ifp_egm.md similarity index 100% rename from lectures/ifp.md rename to lectures/ifp_egm.md diff --git a/lectures/mccall_correlated.md b/lectures/mccall_persist_trans.md similarity index 100% rename from lectures/mccall_correlated.md rename to lectures/mccall_persist_trans.md diff --git a/lectures/optgrowth_fast.md b/lectures/optgrowth_fast.md deleted file mode 100644 index 2ac1e130..00000000 --- a/lectures/optgrowth_fast.md +++ /dev/null @@ -1,382 +0,0 @@ ---- -jupytext: - text_representation: - extension: .md - format_name: myst -kernelspec: - display_name: Python 3 - language: python - name: python3 ---- - -(optgrowth)= -```{raw} jupyter - -``` - -# {index}`最优增长 II:使用Numba加速代码 ` - -```{contents} 目录 -:depth: 2 -``` - -除了Anaconda中已有的库外,本讲座还需要以下库: - -```{code-cell} ipython ---- -tags: [hide-output] ---- -!pip install quantecon -``` - -## 概述 - -在{doc}`上一讲 `中,我们研究了一个具有代表性个体的随机最优增长模型。 - -我们使用动态规划方法对该模型进行了求解。 - -在编写代码时,我们注重清晰性和灵活性。 - -尽管这些特性十分重要,但在实际应用中,灵活性与运行速度之间往往存在权衡。 - -其原因在于,当代码的灵活性降低时,我们能够更容易地利用模型的结构特征。 - -(这一点在算法与数学问题中普遍成立:越具体的问题往往具有更强的结构特征,而经过适当设计,这些结构可被有效利用,从而获得更优的结果。) - -因此,在本讲中,我们将牺牲一定的灵活性以换取更高的运行速度,采用即时(JIT)编译来加速代码的执行。 - -下面让我们从导入相关库开始: - -```{code-cell} ipython -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, jit -from quantecon.optimize.scalar_maximization import brent_max -``` - -函数`brent_max`同样被设计用于嵌入JIT编译代码中。 - -这些函数可作为SciPy中类似函数的替代方案(遗憾的是,SciPy中的相关函数目前尚不支持JIT)。 - -## 模型 - -```{index} single: Optimal Growth; Model -``` - -本节所使用的模型与我们在{doc}`前一讲 `关于最优增长的讲授中所讨论的模型相同。 - -我们从对数型效用函数开始: - -$$ -u(c) = \ln(c) -$$ - -并继续作如下假设: - -* 生产函数为 $f(k) = k^{\alpha}$; -* 随机冲击项 $\xi$ 的分布为 $\phi$,其中 $\xi := \exp(\mu + s \zeta)$ 且 $\zeta$ 为标准正态分布。 - -我们将再次使用价值函数迭代(VFI)来求解这个模型。 - -具体来说,算法保持不变,唯一的区别在于具体的实现方式。 - -和之前一样,我们会对比本次计算所得结果与真实解。 - -```{code-cell} ipython3 -:load: _static/lecture_specific/optgrowth/cd_analytical.py -``` - -## 计算 - -```{index} single: Dynamic Programming; Computation -``` - -我们将再次把最优增长模型的基本要素封装在一个类中。 - -然而,与此前不同的是,我们将使用[Numba](https://python-programming.quantecon.org/numba.html)的 `@jitclass`装饰器来对该类进行JIT编译。 - -由于我们计划使用Numba来编译该类,因此需要明确指定数据类型。 - -在代码中,你将看到一个名为`opt_growth_data`的列表,该列表定义在类的上方,用于说明这些类型。 - -与{doc}`上一讲`不同的是,这里我们将生产和效用函数的具体形式直接写入类中,而非保持一般性形式。 - -也就是说,我们在此牺牲了一定的灵活性,以换取更高的运行速度。 - -```{code-cell} ipython3 -:load: _static/lecture_specific/optgrowth_fast/ogm.py -``` - -该类还包含若干方法,例如`u_prime`,虽然在当前讲义中尚未使用,但将在后续课程中发挥作用。 - -### 贝尔曼算子 - -我们将使用JIT编译来加速贝尔曼算子的计算。 - -首先,定义一个函数,用于计算在给定状态`y`下,某一特定消费选择`c`所对应的价值。该函数基于贝尔曼方程{eq}`fpb30`。 - -```{code-cell} ipython3 -@jit -def state_action_value(c, y, v_array, og): - """ - 贝尔曼方程右侧。 - - * c是消费 - * y是收入 - * og是OptimalGrowthModel的一个实例 - * v_array表示网格上的值函数猜测 - - """ - - u, f, β, shocks = og.u, og.f, og.β, og.shocks - - v = lambda x: np.interp(x, og.grid, v_array) - - return u(c) + β * np.mean(v(f(y - c) * shocks)) -``` - -现在我们可以实现贝尔曼算子,它用于最大化贝尔曼方程的右侧: - -```{code-cell} ipython3 -@jit -def T(v, og): - """ - 贝尔曼算子。 - - * og 是 OptimalGrowthModel 的一个实例 - * v 是一个数组,表示价值函数的猜测值 - - """ - - v_new = np.empty_like(v) - v_greedy = np.empty_like(v) - - for i in range(len(og.grid)): - y = og.grid[i] - - # 在状态 y 下最大化贝尔曼方程的右侧 - result = brent_max(state_action_value, 1e-10, y, args=(y, v, og)) - v_greedy[i], v_new[i] = result[0], result[1] - - return v_greedy, v_new -``` - -我们使用`solve_model`函数进行迭代直到收敛。 - -```{code-cell} ipython3 -:load: _static/lecture_specific/optgrowth/solve_model.py -``` - -让我们用默认参数计算近似解。 - -首先创建一个实例: - -```{code-cell} ipython3 -og = OptimalGrowthModel() -``` - -现在我们调用`solve_model`,使用`%%time`魔法指令来记录运行时间。 - -```{code-cell} ipython3 -%%time -v_greedy, v_solution = solve_model(og) -``` - -你会发现,这比我们的{doc}`原始实现 `要*快得多*。 - -下面,生成近似策略与真实策略的对比图: - -```{code-cell} ipython3 -fig, ax = plt.subplots() - -ax.plot(og.grid, v_greedy, lw=2, - alpha=0.8, label='近似策略函数') - -ax.plot(og.grid, σ_star(og.grid, og.α, og.β), 'k--', - lw=2, alpha=0.8, label='真实策略函数') - -ax.legend() -plt.show() -``` - -与之前一样,拟合效果非常好 --- 这是意料之中的,因为我们没有改变算法。 - -两种策略之间的最大绝对偏差是 - -```{code-cell} ipython3 -np.max(np.abs(v_greedy - σ_star(og.grid, og.α, og.β))) -``` - -## 练习 - -```{exercise} -:label: ogfast_ex1 - -在默认参数设定下,从给定的初始条件 $v(y) = u(y)$ 开始,对贝尔曼算子进行 20 次迭代,并记录整个迭代过程所耗费的时间。 -``` - -```{solution-start} ogfast_ex1 -:class: dropdown -``` - -设置初始条件: - -```{code-cell} ipython3 -v = og.u(og.grid) -``` - -计时: - -```{code-cell} ipython3 -%%time - -for i in range(20): - v_greedy, v_new = T(v, og) - v = v_new -``` - -与非编译版本的价值函数迭代的{ref}`用时 `相比,JIT编译的代码通常快一个数量级。 - -```{solution-end} -``` - -```{exercise} -:label: ogfast_ex2 - -将最优增长模型修改为采用CRRA效用函数的设定: - -$$ -u(c) = \frac{c^{1 - \gamma} } {1 - \gamma} -$$ - -设定`γ = 1.5`为默认值,并保持其他模型设定不变。 - -(注意,`jitclass`目前不支持类继承,因此你需要复制原有的类,并相应修改相关的参数与方法。) - -计算最优策略的估计值,并绘制其图像。将所得图像与第一讲最优增长模型中{ref}`对应练习 `的图表进行比较。 - -同时,对比两种实现的运行时间。 -``` - -```{solution-start} ogfast_ex2 -:class: dropdown -``` - -这是CRRA版本的`OptimalGrowthModel`: - -```{code-cell} ipython3 -:load: _static/lecture_specific/optgrowth_fast/ogm_crra.py -``` - -创建一个实例: - -```{code-cell} ipython3 -og_crra = OptimalGrowthModel_CRRA() -``` - -调用`solve_model`,使用`%%time`魔术命令来记录运行时间。 - -```{code-cell} ipython3 -%%time -v_greedy, v_solution = solve_model(og_crra) -``` - -以下是得到的策略图: - -```{code-cell} ipython3 -fig, ax = plt.subplots() - -ax.plot(og.grid, v_greedy, lw=2, - alpha=0.6, label='近似价值函数') - -ax.legend(loc='lower right') -plt.show() -``` - -这与我们在{ref}`练习 `中使用非jit代码得到的答案相符,但执行时间快了一个数量级。 - -```{solution-end} -``` - - -```{exercise-start} -:label: ogfast_ex3 -``` - -在本练习中,我们回到最初的对数型效用函数设定。 - -当给定最优消费政策 $\sigma$ 后,收入的动态演化如下: - -$$ -y_{t+1} = f(y_t - \sigma(y_t)) \xi_{t+1} -$$ - -下图展示了该序列在三种不同贴现因子(因而对应三种不同政策)下的模拟结果,每个序列包含 100 个样本点。 - -```{figure} /_static/lecture_specific/optgrowth/solution_og_ex2.png -``` - -在每个序列中,初始条件都是 $y_0 = 0.1$。 - -贴现因子分别为`discount_factors = (0.8, 0.9, 0.98)`。 - -我们还通过设置`s = 0.05`稍微降低了冲击的幅度。 - -除此之外,参数和原始设定与前面讨论的对数线性模型相同。 - -注意,更有耐心的个体通常拥有更高的财富。 - -请在保持随机性结构的前提下,复现该图像。 - -```{exercise-end} -``` - -```{solution-start} ogfast_ex3 -:class: dropdown -``` - -参考答案: - -```{code-cell} ipython3 -def simulate_og(σ_func, og, y0=0.1, ts_length=100): - ''' - 根据消费策略σ计算时间序列。 - ''' - y = np.empty(ts_length) - ξ = np.random.randn(ts_length-1) - y[0] = y0 - for t in range(ts_length-1): - y[t+1] = (y[t] - σ_func(y[t]))**og.α * np.exp(og.μ + og.s * ξ[t]) - return y -``` - -```{code-cell} ipython3 -fig, ax = plt.subplots() - -for β in (0.8, 0.9, 0.98): - - og = OptimalGrowthModel(β=β, s=0.05) - - v_greedy, v_solution = solve_model(og, verbose=False) - - # 定义最优策略函数 - σ_func = lambda x: np.interp(x, og.grid, v_greedy) - y = simulate_og(σ_func, og) - ax.plot(y, lw=2, alpha=0.6, label=rf'$\beta = {β}$') - -ax.legend(loc='lower right') -plt.show() -``` - -```{solution-end} -``` - diff --git a/lectures/cake_eating_problem.md b/lectures/os.md similarity index 100% rename from lectures/cake_eating_problem.md rename to lectures/os.md diff --git a/lectures/cake_eating_numerical.md b/lectures/os_numerical.md similarity index 100% rename from lectures/cake_eating_numerical.md rename to lectures/os_numerical.md diff --git a/lectures/optgrowth.md b/lectures/os_stochastic.md similarity index 100% rename from lectures/optgrowth.md rename to lectures/os_stochastic.md