Skip to content

Commit dd56a77

Browse files
committed
[cass_koopmans_1] Claude review of issue #457
1 parent e024f92 commit dd56a77

File tree

1 file changed

+99
-18
lines changed

1 file changed

+99
-18
lines changed

lectures/cass_koopmans_1.md

Lines changed: 99 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -782,7 +782,24 @@ the optimal capital stock stays close to
782782
its steady state value most of the time.
783783
784784
```{code-cell} ipython3
785-
plot_paths(pp, 0.3, k_ss/3, [250, 150, 50, 25], k_ss=k_ss);
785+
# 34.6. A Turnpike Property
786+
787+
# The following calculation indicates that when T is very large, the optimal capital
788+
# stock stays close to its steady state value most of the time, REGARDLESS of the
789+
# initial capital stock K_0.
790+
791+
# Let's demonstrate this key property by starting from multiple different initial
792+
# capital levels, both above and below the steady state:
793+
794+
k0_values = [k_ss/4, k_ss/2, k_ss*0.8, k_ss*1.2, k_ss*1.5, k_ss*2]
795+
796+
print("Demonstrating Turnpike Property with different initial capital stocks:")
797+
for i, k0 in enumerate(k0_values):
798+
ratio = k0/k_ss
799+
print(f" K_0_{i+1} = {k0:.2f} ({ratio:.1f} × K_ss)")
800+
801+
# Plot optimal paths from different starting points
802+
plot_saving_rate(pp, 0.3, k0_values, [200], k_ter=k_ss, k_ss=k_ss, s_ss=s_ss);
786803
```
787804
788805
In the above graphs, different colors are associated with
@@ -793,10 +810,26 @@ closer to the steady state value $\bar K$ for longer.
793810
794811
This pattern reflects a **turnpike** property of the steady state.
795812
796-
A rule of thumb for the planner is
813+
Notice how all paths, regardless of their starting point, converge to and stay
814+
near the steady state for most of the time horizon. This demonstrates the key
815+
insight of the turnpike property:
797816
798-
- from $K_0$, push $K_t$ toward
799-
the steady state and stay close to the steady state until time approaches $T$.
817+
1. **Path Independence**: The long-run behavior is independent of initial conditions
818+
2. **Convergence**: All paths converge to the steady state neighborhood
819+
3. **Persistence**: Paths stay near steady state for most of the horizon
820+
821+
Let's also compare different time horizons starting from the same initial condition
822+
to show how longer horizons lead to more time spent near the steady state:
823+
824+
```{code-cell} ipython3
825+
plot_saving_rate(pp, 0.3, k_ss/3, [250, 150, 75, 50], k_ter=k_ss, k_ss=k_ss, s_ss=s_ss)
826+
827+
```
828+
829+
A rule of thumb for the planner is:
830+
1. From any K_0, push K_t toward the steady state
831+
2. Stay close to the steady state until time approaches T
832+
3. Then optimally wind down capital to meet terminal condition K_{T+1} = K_ter
800833
801834
The planner accomplishes this by adjusting the saving rate $\frac{f(K_t) - C_t}{f(K_t)}$
802835
over time.
@@ -813,20 +846,68 @@ def saving_rate(pp, c_path, k_path):
813846
```
814847
815848
```{code-cell} ipython3
816-
def plot_saving_rate(pp, c0, k0, T_arr, k_ter=0, k_ss=None, s_ss=None):
817-
818-
fix, axs = plt.subplots(2, 2, figsize=(12, 9))
819-
820-
c_paths, k_paths = plot_paths(pp, c0, k0, T_arr, k_ter=k_ter, k_ss=k_ss, axs=axs.flatten())
821-
822-
for i, T in enumerate(T_arr):
823-
s_path = saving_rate(pp, c_paths[i], k_paths[i])
824-
axs[1, 1].plot(s_path)
825-
826-
axs[1, 1].set(xlabel='t', ylabel='$s_t$', title='Saving rate')
827-
828-
if s_ss is not None:
829-
axs[1, 1].hlines(s_ss, 0, np.max(T_arr), linestyle='--')
849+
def plot_saving_rate(pp, c0, k0_arr, T_arr, k_ter=0, k_ss=None, s_ss=None):
850+
"""
851+
Enhanced saving rate plotting function that accepts either:
852+
- A single k0 value (backward compatibility)
853+
- A list of k0 values to demonstrate turnpike property
854+
"""
855+
# Handle backward compatibility - if k0_arr is a scalar, convert to list
856+
if np.isscalar(k0_arr):
857+
k0_arr = [k0_arr]
858+
single_k0 = True
859+
else:
860+
single_k0 = False
861+
862+
if single_k0:
863+
# Original behavior for single k0
864+
fig, axs = plt.subplots(2, 2, figsize=(12, 9))
865+
c_paths, k_paths = plot_paths(pp, c0, k0_arr[0], T_arr, k_ter=k_ter, k_ss=k_ss, axs=axs.flatten())
866+
for i, T in enumerate(T_arr):
867+
s_path = saving_rate(pp, c_paths[i], k_paths[i])
868+
axs[1, 1].plot(s_path)
869+
axs[1, 1].set(xlabel='t', ylabel='$s_t$', title='Saving rate')
870+
if s_ss is not None:
871+
axs[1, 1].hlines(s_ss, 0, np.max(T_arr), linestyle='--')
872+
else:
873+
# Enhanced behavior for multiple k0 values
874+
fig, axs = plt.subplots(2, 2, figsize=(15, 10))
875+
876+
# Use the largest T for the main comparison
877+
T_main = max(T_arr)
878+
879+
# Plot consumption, capital, and multiplier paths for different k0 values
880+
colors = plt.cm.viridis(np.linspace(0, 1, len(k0_arr)))
881+
ylabels = ['$c_t$', '$k_t$', '$\mu_t$']
882+
titles = ['Consumption', 'Capital', 'Lagrange Multiplier']
883+
884+
for i, k0 in enumerate(k0_arr):
885+
c_vec, k_vec = bisection(pp, c0, k0, T_main, k_ter=k_ter, verbose=False)
886+
μ_vec = pp.u_prime(c_vec)
887+
paths = [c_vec, k_vec, μ_vec]
888+
889+
for j in range(3):
890+
axs[j//2, j%2].plot(paths[j], color=colors[i],
891+
label=f'$K_0$ = {k0:.2f}' if j == 1 else "")
892+
axs[j//2, j%2].set(xlabel='t', ylabel=ylabels[j], title=titles[j])
893+
894+
# Plot saving rate
895+
s_path = saving_rate(pp, c_vec, k_vec)
896+
axs[1, 1].plot(s_path, color=colors[i], label=f'$K_0$ = {k0:.2f}')
897+
898+
# Add steady state lines and legends
899+
if k_ss is not None:
900+
axs[0, 1].axhline(k_ss, c='k', ls='--', lw=1, label='Steady state')
901+
902+
if s_ss is not None:
903+
axs[1, 1].axhline(s_ss, c='k', ls='--', lw=1, label='Steady state')
904+
905+
axs[0, 1].legend()
906+
axs[1, 1].set(xlabel='t', ylabel='$s_t$', title='Saving rate')
907+
axs[1, 1].legend()
908+
909+
plt.tight_layout()
910+
plt.show()
830911
```
831912
832913
```{code-cell} ipython3

0 commit comments

Comments
 (0)