@@ -782,7 +782,24 @@ the optimal capital stock stays close to
782782its 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
788805In the above graphs, different colors are associated with
@@ -793,10 +810,26 @@ closer to the steady state value $\bar K$ for longer.
793810
794811This 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
801834The planner accomplishes this by adjusting the saving rate $\frac{f(K_t) - C_t}{f(K_t)}$
802835over 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