diff --git a/lectures/_static/lecture_specific/long_run_growth/tooze_ch1_graph.png b/lectures/_static/lecture_specific/long_run_growth/tooze_ch1_graph.png index 3ae6891e3..45ca55bb8 100644 Binary files a/lectures/_static/lecture_specific/long_run_growth/tooze_ch1_graph.png and b/lectures/_static/lecture_specific/long_run_growth/tooze_ch1_graph.png differ diff --git a/lectures/ar1_processes.md b/lectures/ar1_processes.md index 1a041a526..786e22107 100644 --- a/lectures/ar1_processes.md +++ b/lectures/ar1_processes.md @@ -36,7 +36,7 @@ These simple models are used again and again in economic research to represent t * productivity, etc. We are going to study AR(1) processes partly because they are useful and -partly because they help us understand important concepts. +partly because they help us understand important concepts. Let's start with some imports: @@ -56,14 +56,14 @@ The **AR(1) model** (autoregressive model of order 1) takes the form X_{t+1} = a X_t + b + c W_{t+1} ``` -where $a, b, c$ are scalar-valued parameters +where $a, b, c$ are scalar-valued parameters (Equation {eq}`can_ar1` is sometimes called a **stochastic difference equation**.) ```{prf:example} :label: ar1_ex_ar -For example, $X_t$ might be +For example, $X_t$ might be * the log of labor income for a given household, or * the log of money demand in a given economy. @@ -356,9 +356,7 @@ In this equation, we can use observed data to evaluate the left hand side of {eq And we can use a theoretical AR(1) model to calculate the right hand side. -If $\frac{1}{m} \sum_{t = 1}^m X_t$ is not close to $\psi^*(x)$, even for many -observations, then our theory seems to be incorrect and we will need to revise -it. +If $\frac{1}{m} \sum_{t = 1}^m h(X_t)$ is not close to $\int h(x)\psi^*(x) dx$, even for many observations, then our theory seems to be incorrect and we will need to revise it. ## Exercises @@ -427,8 +425,8 @@ def true_moments_ar1(k): return 0 k_vals = np.arange(6) + 1 -sample_moments = np.empty_like(k_vals) -true_moments = np.empty_like(k_vals) +sample_moments = np.empty(len(k_vals), dtype=float) +true_moments = np.empty(len(k_vals), dtype=float) for k_idx, k in enumerate(k_vals): sample_moments[k_idx] = sample_moments_ar1(k) @@ -464,8 +462,8 @@ $$ For $K$ use the Gaussian kernel ($K$ is the standard normal density). -Write the class so that the bandwidth defaults to Silverman’s rule (see -the “rule of thumb” discussion on [this +Write the class so that the bandwidth defaults to Silverman's rule (see +the "rule of thumb" discussion on [this page](https://en.wikipedia.org/wiki/Kernel_density_estimation)). Test the class you have written by going through the steps diff --git a/lectures/business_cycle.md b/lectures/business_cycle.md index 89f72d7f8..6b9dc22ea 100644 --- a/lectures/business_cycle.md +++ b/lectures/business_cycle.md @@ -48,7 +48,7 @@ Here's some minor code to help with colors in our plots. :tags: [hide-input] # Set graphical parameters -cycler = plt.cycler(linestyle=['-', '-.', '--', ':'], +cycler = plt.cycler(linestyle=['-', '-.', '--', ':'], color=['#377eb8', '#ff7f00', '#4daf4a', '#ff334f']) plt.rc('axes', prop_cycle=cycler) ``` @@ -72,7 +72,7 @@ Now we use this series ID to obtain the data. ```{code-cell} ipython3 gdp_growth = wb.data.DataFrame('NY.GDP.MKTP.KD.ZG', - ['USA', 'ARG', 'GBR', 'GRC', 'JPN'], + ['USA', 'ARG', 'GBR', 'GRC', 'JPN'], labels=True) gdp_growth ``` @@ -91,14 +91,14 @@ wb.series.metadata.get('NY.GDP.MKTP.KD.ZG') (gdp_growth)= ## GDP growth rate -First we look at GDP growth. +First we look at GDP growth. Let's source our data from the World Bank and clean it. ```{code-cell} ipython3 # Use the series ID retrieved before gdp_growth = wb.data.DataFrame('NY.GDP.MKTP.KD.ZG', - ['USA', 'ARG', 'GBR', 'GRC', 'JPN'], + ['USA', 'ARG', 'GBR', 'GRC', 'JPN'], labels=True) gdp_growth = gdp_growth.set_index('Country') gdp_growth.columns = gdp_growth.columns.str.replace('YR', '').astype(int) @@ -115,11 +115,11 @@ We write a function to generate plots for individual countries taking into accou ```{code-cell} ipython3 :tags: [hide-input] -def plot_series(data, country, ylabel, +def plot_series(data, country, ylabel, txt_pos, ax, g_params, b_params, t_params, ylim=15, baseline=0): """ - Plots a time series with recessions highlighted. + Plots a time series with recessions highlighted. Parameters ---------- @@ -143,7 +143,7 @@ def plot_series(data, country, ylabel, Parameters for the recession labels baseline : float, optional Dashed baseline on the plot, by default 0 - + Returns ------- ax : matplotlib.axes.Axes @@ -151,7 +151,7 @@ def plot_series(data, country, ylabel, """ ax.plot(data.loc[country], label=country, **g_params) - + # Highlight recessions ax.axvspan(1973, 1975, **b_params) ax.axvspan(1990, 1992, **b_params) @@ -162,27 +162,27 @@ def plot_series(data, country, ylabel, else: ylim = ax.get_ylim()[1] ax.text(1974, ylim + ylim*txt_pos, - 'Oil Crisis\n(1974)', **t_params) + 'Oil Crisis\n(1974)', **t_params) ax.text(1991, ylim + ylim*txt_pos, - '1990s recession\n(1991)', **t_params) + '1990s recession\n(1991)', **t_params) ax.text(2008, ylim + ylim*txt_pos, - 'GFC\n(2008)', **t_params) + 'GFC\n(2008)', **t_params) ax.text(2020, ylim + ylim*txt_pos, 'Covid-19\n(2020)', **t_params) # Add a baseline for reference if baseline != None: - ax.axhline(y=baseline, - color='black', + ax.axhline(y=baseline, + color='black', linestyle='--') ax.set_ylabel(ylabel) ax.legend() return ax -# Define graphical parameters +# Define graphical parameters g_params = {'alpha': 0.7} b_params = {'color':'grey', 'alpha': 0.2} -t_params = {'color':'grey', 'fontsize': 9, +t_params = {'color':'grey', 'fontsize': 9, 'va':'center', 'ha':'center'} ``` @@ -201,8 +201,8 @@ fig, ax = plt.subplots() country = 'United States' ylabel = 'GDP growth rate (%)' -plot_series(gdp_growth, country, - ylabel, 0.1, ax, +plot_series(gdp_growth, country, + ylabel, 0.1, ax, g_params, b_params, t_params) plt.show() ``` @@ -233,8 +233,8 @@ mystnb: fig, ax = plt.subplots() country = 'United Kingdom' -plot_series(gdp_growth, country, - ylabel, 0.1, ax, +plot_series(gdp_growth, country, + ylabel, 0.1, ax, g_params, b_params, t_params) plt.show() ``` @@ -258,8 +258,8 @@ mystnb: fig, ax = plt.subplots() country = 'Japan' -plot_series(gdp_growth, country, - ylabel, 0.1, ax, +plot_series(gdp_growth, country, + ylabel, 0.1, ax, g_params, b_params, t_params) plt.show() ``` @@ -277,8 +277,8 @@ mystnb: fig, ax = plt.subplots() country = 'Greece' -plot_series(gdp_growth, country, - ylabel, 0.1, ax, +plot_series(gdp_growth, country, + ylabel, 0.1, ax, g_params, b_params, t_params) plt.show() ``` @@ -299,8 +299,8 @@ mystnb: fig, ax = plt.subplots() country = 'Argentina' -plot_series(gdp_growth, country, - ylabel, 0.1, ax, +plot_series(gdp_growth, country, + ylabel, 0.1, ax, g_params, b_params, t_params) plt.show() ``` @@ -324,15 +324,15 @@ We study unemployment using rate data from FRED spanning from [1929-1942](https: start_date = datetime.datetime(1929, 1, 1) end_date = datetime.datetime(1942, 6, 1) -unrate_history = web.DataReader('M0892AUSM156SNBR', +unrate_history = web.DataReader('M0892AUSM156SNBR', 'fred', start_date,end_date) -unrate_history.rename(columns={'M0892AUSM156SNBR': 'UNRATE'}, +unrate_history.rename(columns={'M0892AUSM156SNBR': 'UNRATE'}, inplace=True) start_date = datetime.datetime(1948, 1, 1) end_date = datetime.datetime(2022, 12, 31) -unrate = web.DataReader('UNRATE', 'fred', +unrate = web.DataReader('UNRATE', 'fred', start_date, end_date) ``` @@ -348,7 +348,7 @@ mystnb: tags: [hide-input] --- -# We use the census bureau's estimate for the unemployment rate +# We use the census bureau's estimate for the unemployment rate # between 1942 and 1948 years = [datetime.datetime(year, 6, 1) for year in range(1942, 1948)] unrate_census = [4.7, 1.9, 1.2, 1.9, 3.9, 3.9] @@ -365,24 +365,24 @@ nber = web.DataReader('USREC', 'fred', start_date, end_date) fig, ax = plt.subplots() -ax.plot(unrate_history, **g_params, - color='#377eb8', +ax.plot(unrate_history, **g_params, + color='#377eb8', linestyle='-', linewidth=2) -ax.plot(unrate_census, **g_params, - color='black', linestyle='--', +ax.plot(unrate_census, **g_params, + color='black', linestyle='--', label='Census estimates', linewidth=2) -ax.plot(unrate, **g_params, color='#377eb8', +ax.plot(unrate, **g_params, color='#377eb8', linestyle='-', linewidth=2) # Draw gray boxes according to NBER recession indicators ax.fill_between(nber.index, 0, 1, - where=nber['USREC']==1, + where=nber['USREC']==1, color='grey', edgecolor='none', - alpha=0.3, - transform=ax.get_xaxis_transform(), + alpha=0.3, + transform=ax.get_xaxis_transform(), label='NBER recession indicators') ax.set_ylim([0, ax.get_ylim()[1]]) -ax.legend(loc='upper center', +ax.legend(loc='upper center', bbox_to_anchor=(0.5, 1.1), ncol=3, fancybox=True, shadow=True) ax.set_ylabel('unemployment rate (%)') @@ -391,15 +391,15 @@ plt.show() ``` -The plot shows that +The plot shows that * expansions and contractions of the labor market have been highly correlated - with recessions. + with recessions. * cycles are, in general, asymmetric: sharp rises in unemployment are followed by slow recoveries. It also shows us how unique labor market conditions were in the US during the -post-pandemic recovery. +post-pandemic recovery. The labor market recovered at an unprecedented rate after the shock in 2020-2021. @@ -408,11 +408,11 @@ The labor market recovered at an unprecedented rate after the shock in 2020-2021 ## Synchronization In our {ref}`previous discussion`, we found that developed economies have had -relatively synchronized periods of recession. +relatively synchronized periods of recession. -At the same time, this synchronization did not appear in Argentina until the 2000s. +At the same time, this synchronization did not appear in Argentina until the 2000s. -Let's examine this trend further. +Let's examine this trend further. With slight modifications, we can use our previous function to draw a plot that includes multiple countries. @@ -423,9 +423,9 @@ tags: [hide-input] --- -def plot_comparison(data, countries, - ylabel, txt_pos, y_lim, ax, - g_params, b_params, t_params, +def plot_comparison(data, countries, + ylabel, txt_pos, y_lim, ax, + g_params, b_params, t_params, baseline=0): """ Plot multiple series on the same graph @@ -452,17 +452,17 @@ def plot_comparison(data, countries, Parameters for the recession labels baseline : float, optional Dashed baseline on the plot, by default 0 - + Returns ------- ax : matplotlib.axes.Axes Axes with the plot. """ - + # Allow the function to go through more than one series for country in countries: ax.plot(data.loc[country], label=country, **g_params) - + # Highlight recessions ax.axvspan(1973, 1975, **b_params) ax.axvspan(1990, 1992, **b_params) @@ -471,26 +471,26 @@ def plot_comparison(data, countries, if y_lim != None: ax.set_ylim([-y_lim, y_lim]) ylim = ax.get_ylim()[1] - ax.text(1974, ylim + ylim*txt_pos, - 'Oil Crisis\n(1974)', **t_params) - ax.text(1991, ylim + ylim*txt_pos, - '1990s recession\n(1991)', **t_params) - ax.text(2008, ylim + ylim*txt_pos, - 'GFC\n(2008)', **t_params) - ax.text(2020, ylim + ylim*txt_pos, - 'Covid-19\n(2020)', **t_params) + ax.text(1974, ylim + ylim*txt_pos, + 'Oil Crisis\n(1974)', **t_params) + ax.text(1991, ylim + ylim*txt_pos, + '1990s recession\n(1991)', **t_params) + ax.text(2008, ylim + ylim*txt_pos, + 'GFC\n(2008)', **t_params) + ax.text(2020, ylim + ylim*txt_pos, + 'Covid-19\n(2020)', **t_params) if baseline != None: - ax.hlines(y=baseline, xmin=ax.get_xlim()[0], - xmax=ax.get_xlim()[1], color='black', + ax.hlines(y=baseline, xmin=ax.get_xlim()[0], + xmax=ax.get_xlim()[1], color='black', linestyle='--') ax.set_ylabel(ylabel) ax.legend() return ax -# Define graphical parameters +# Define graphical parameters g_params = {'alpha': 0.7} b_params = {'color':'grey', 'alpha': 0.2} -t_params = {'color':'grey', 'fontsize': 9, +t_params = {'color':'grey', 'fontsize': 9, 'va':'center', 'ha':'center'} ``` @@ -503,7 +503,7 @@ tags: [hide-input] # Obtain GDP growth rate for a list of countries gdp_growth = wb.data.DataFrame('NY.GDP.MKTP.KD.ZG', - ['CHN', 'USA', 'DEU', 'BRA', 'ARG', 'GBR', 'JPN', 'MEX'], + ['CHN', 'USA', 'DEU', 'BRA', 'ARG', 'GBR', 'JPN', 'MEX'], labels=True) gdp_growth = gdp_growth.set_index('Country') gdp_growth.columns = gdp_growth.columns.str.replace('YR', '').astype(int) @@ -524,9 +524,9 @@ tags: [hide-input] fig, ax = plt.subplots() countries = ['United Kingdom', 'United States', 'Germany', 'Japan'] ylabel = 'GDP growth rate (%)' -plot_comparison(gdp_growth.loc[countries, 1962:], +plot_comparison(gdp_growth.loc[countries, 1962:], countries, ylabel, - 0.1, 20, ax, + 0.1, 20, ax, g_params, b_params, t_params) plt.show() ``` @@ -544,27 +544,27 @@ tags: [hide-input] fig, ax = plt.subplots() countries = ['Brazil', 'China', 'Argentina', 'Mexico'] -plot_comparison(gdp_growth.loc[countries, 1962:], - countries, ylabel, - 0.1, 20, ax, +plot_comparison(gdp_growth.loc[countries, 1962:], + countries, ylabel, + 0.1, 20, ax, g_params, b_params, t_params) plt.show() ``` -The comparison of GDP growth rates above suggests that +The comparison of GDP growth rates above suggests that business cycles are becoming more synchronized in 21st-century recessions. However, emerging and less developed economies often experience more volatile -changes throughout the economic cycles. +changes throughout the economic cycles. Despite the synchronization in GDP growth, the experience of individual countries during -the recession often differs. +the recession often differs. We use the unemployment rate and the recovery of labor market conditions as another example. -Here we compare the unemployment rate of the United States, +Here we compare the unemployment rate of the United States, the United Kingdom, Japan, and France. ```{code-cell} ipython3 @@ -585,8 +585,8 @@ fig, ax = plt.subplots() countries = ['United Kingdom', 'United States', 'Japan', 'France'] ylabel = 'unemployment rate (national estimate) (%)' -plot_comparison(unempl_rate, countries, - ylabel, 0.05, None, ax, g_params, +plot_comparison(unempl_rate, countries, + ylabel, 0.05, None, ax, g_params, b_params, t_params, baseline=None) plt.show() ``` @@ -597,10 +597,10 @@ relatively slow labor market recoveries after negative shocks. We also notice that Japan has a history of very low and stable unemployment rates. -## Leading indicators and correlated factors +## Leading indicators and correlated factors Examining leading indicators and correlated factors helps policymakers to -understand the causes and results of business cycles. +understand the causes and results of business cycles. We will discuss potential leading indicators and correlated factors from three perspectives: consumption, production, and credit level. @@ -609,13 +609,13 @@ perspectives: consumption, production, and credit level. ### Consumption Consumption depends on consumers' confidence in their -income and the overall performance of the economy in the future. +income and the overall performance of the economy in the future. One widely cited indicator for consumer confidence is the [consumer sentiment index](https://fred.stlouisfed.org/series/UMCSENT) published by the University of Michigan. Here we plot the University of Michigan Consumer Sentiment Index and -year-on-year +year-on-year [core consumer price index](https://fred.stlouisfed.org/series/CPILFESL) (CPI) change from 1978-2022 in the US. @@ -636,39 +636,39 @@ start_date_graph = datetime.datetime(1977, 1, 1) end_date_graph = datetime.datetime(2023, 12, 31) nber = web.DataReader('USREC', 'fred', start_date, end_date) -consumer_confidence = web.DataReader('UMCSENT', 'fred', +consumer_confidence = web.DataReader('UMCSENT', 'fred', start_date, end_date) fig, ax = plt.subplots() -ax.plot(consumer_confidence, **g_params, - color='#377eb8', linestyle='-', +ax.plot(consumer_confidence, **g_params, + color='#377eb8', linestyle='-', linewidth=2) -ax.fill_between(nber.index, 0, 1, - where=nber['USREC']==1, +ax.fill_between(nber.index, 0, 1, + where=nber['USREC']==1, color='grey', edgecolor='none', - alpha=0.3, - transform=ax.get_xaxis_transform(), + alpha=0.3, + transform=ax.get_xaxis_transform(), label='NBER recession indicators') ax.set_ylim([0, ax.get_ylim()[1]]) ax.set_ylabel('consumer sentiment index') # Plot CPI on another y-axis ax_t = ax.twinx() -inflation = web.DataReader('CPILFESL', 'fred', +inflation = web.DataReader('CPILFESL', 'fred', start_date, end_date).pct_change(12)*100 # Add CPI on the legend without drawing the line again -ax_t.plot(2020, 0, **g_params, linestyle='-', +ax_t.plot(2020, 0, **g_params, linestyle='-', linewidth=2, label='consumer sentiment index') -ax_t.plot(inflation, **g_params, - color='#ff7f00', linestyle='--', +ax_t.plot(inflation, **g_params, + color='#ff7f00', linestyle='--', linewidth=2, label='CPI YoY change (%)') ax_t.fill_between(nber.index, 0, 1, - where=nber['USREC']==1, + where=nber['USREC']==1, color='grey', edgecolor='none', - alpha=0.3, - transform=ax.get_xaxis_transform(), + alpha=0.3, + transform=ax.get_xaxis_transform(), label='NBER recession indicators') ax_t.set_ylim([0, ax_t.get_ylim()[1]]) ax_t.set_xlim([start_date_graph, end_date_graph]) @@ -679,7 +679,7 @@ ax_t.set_ylabel('CPI YoY change (%)') plt.show() ``` -We see that +We see that * consumer sentiment often remains high during expansions and drops before recessions. @@ -693,12 +693,12 @@ This trend is more significant during [stagflation](https://en.wikipedia.org/wik ### Production -Real industrial output is highly correlated with recessions in the economy. +Real industrial output is highly correlated with recessions in the economy. -However, it is not a leading indicator, as the peak of contraction in production +However, it is not a leading indicator, as the peak of contraction in production is delayed relative to consumer confidence and inflation. -We plot the real industrial output change from the previous year +We plot the real industrial output change from the previous year from 1919 to 2022 in the US to show this trend. ```{code-cell} ipython3 @@ -713,20 +713,20 @@ tags: [hide-input] start_date = datetime.datetime(1919, 1, 1) end_date = datetime.datetime(2022, 12, 31) -nber = web.DataReader('USREC', 'fred', +nber = web.DataReader('USREC', 'fred', start_date, end_date) -industrial_output = web.DataReader('INDPRO', 'fred', +industrial_output = web.DataReader('INDPRO', 'fred', start_date, end_date).pct_change(12)*100 fig, ax = plt.subplots() -ax.plot(industrial_output, **g_params, - color='#377eb8', linestyle='-', +ax.plot(industrial_output, **g_params, + color='#377eb8', linestyle='-', linewidth=2, label='Industrial production index') ax.fill_between(nber.index, 0, 1, - where=nber['USREC']==1, + where=nber['USREC']==1, color='grey', edgecolor='none', - alpha=0.3, - transform=ax.get_xaxis_transform(), + alpha=0.3, + transform=ax.get_xaxis_transform(), label='NBER recession indicators') ax.set_ylim([ax.get_ylim()[0], ax.get_ylim()[1]]) ax.set_ylabel('YoY real output change (%)') @@ -758,7 +758,7 @@ mystnb: tags: [hide-input] --- -private_credit = wb.data.DataFrame('FS.AST.PRVT.GD.ZS', +private_credit = wb.data.DataFrame('FS.AST.PRVT.GD.ZS', ['GBR'], labels=True) private_credit = private_credit.set_index('Country') private_credit.columns = private_credit.columns.str.replace('YR', '').astype(int) @@ -767,8 +767,8 @@ fig, ax = plt.subplots() countries = 'United Kingdom' ylabel = 'credit level (% of GDP)' -ax = plot_series(private_credit, countries, - ylabel, 0.05, ax, g_params, b_params, +ax = plot_series(private_credit, countries, + ylabel, 0.05, ax, g_params, b_params, t_params, ylim=None, baseline=None) plt.show() ``` diff --git a/lectures/cagan_adaptive.md b/lectures/cagan_adaptive.md index db4335a9b..d216a4869 100644 --- a/lectures/cagan_adaptive.md +++ b/lectures/cagan_adaptive.md @@ -22,7 +22,7 @@ We'll use linear algebra to do some experiments with an alternative "monetarist Like the model in {doc}`cagan_ree`, the model asserts that when a government persistently spends more than it collects in taxes and prints money to finance the shortfall, it puts upward pressure on the price level and generates persistent inflation. -Instead of the "perfect foresight" or "rational expectations" version of the model in {doc}`cagan_ree`, our model in the present lecture is an "adaptive expectations" version of a model that {cite}`Cagan` used to study the monetary dynamics of hyperinflations. +Instead of the "perfect foresight" or "rational expectations" version of the model in {doc}`cagan_ree`, our model in the present lecture is an "adaptive expectations" version of a model that {cite}`Cagan` used to study the monetary dynamics of hyperinflations. It combines these components: @@ -34,7 +34,7 @@ It combines these components: * an exogenous sequence of rates of growth of the money supply -Our model stays quite close to Cagan's original specification. +Our model stays quite close to Cagan's original specification. As in {doc}`pv` and {doc}`cons_smooth`, the only linear algebra operations that we'll be using are matrix multiplication and matrix inversion. @@ -43,7 +43,7 @@ the model. ## Structure of the model -Let +Let * $ m_t $ be the log of the supply of nominal money balances; * $\mu_t = m_{t+1} - m_t $ be the net rate of growth of nominal balances; @@ -52,11 +52,11 @@ Let * $\pi_t^*$ be the public's expected rate of inflation between $t$ and $t+1$; * $T$ the horizon -- i.e., the last period for which the model will determine $p_t$ * $\pi_0^*$ public's initial expected rate of inflation between time $0$ and time $1$. - - + + The demand for real balances $\exp\left(m_t^d-p_t\right)$ is governed by the following version of the Cagan demand function - -$$ + +$$ m_t^d - p_t = -\alpha \pi_t^* \: , \: \alpha > 0 ; \quad t = 0, 1, \ldots, T . $$ (eq:caganmd_ad) @@ -71,7 +71,7 @@ $$ p_t = m_t + \alpha \pi_t^* $$ (eq:eqfiscth1) -Taking the difference between equation {eq}`eq:eqfiscth1` at time $t+1$ and at time +Taking the difference between equation {eq}`eq:eqfiscth1` at time $t+1$ and at time $t$ gives $$ @@ -82,11 +82,11 @@ We assume that the expected rate of inflation $\pi_t^*$ is governed by the following adaptive expectations scheme proposed by {cite}`Friedman1956` and {cite}`Cagan`, where $\lambda\in [0,1]$ denotes the weight on expected inflation. $$ -\pi_{t+1}^* = \lambda \pi_t^* + (1 -\lambda) \pi_t +\pi_{t+1}^* = \lambda \pi_t^* + (1 -\lambda) \pi_t $$ (eq:adaptexpn) As exogenous inputs into the model, we take initial conditions $m_0, \pi_0^*$ -and a money growth sequence $\mu = \{\mu_t\}_{t=0}^T$. +and a money growth sequence $\mu = \{\mu_t\}_{t=0}^T$. As endogenous outputs of our model we want to find sequences $\pi = \{\pi_t\}_{t=0}^T, p = \{p_t\}_{t=0}^T$ as functions of the exogenous inputs. @@ -108,9 +108,9 @@ $$ \pi_1^* \cr \pi_2^* \cr \vdots \cr - \pi_{T+1}^* + \pi_{T+1}^* \end{bmatrix} = - (1-\lambda) \begin{bmatrix} + (1-\lambda) \begin{bmatrix} 0 & 0 & 0 & \cdots & 0 \cr 1 & 0 & 0 & \cdots & 0 \cr 0 & 1 & 0 & \cdots & 0 \cr @@ -132,7 +132,7 @@ are defined implicitly by aligning these two equations. Next we write the key equation {eq}`eq:eqpipi` in matrix notation as -$$ +$$ \begin{bmatrix} \pi_0 \cr \pi_1 \cr \pi_1 \cr \vdots \cr \pi_T \end{bmatrix} = \begin{bmatrix} @@ -141,13 +141,13 @@ $$ 0 & -\alpha & \alpha & \cdots & 0 & 0 \cr 0 & 0 & -\alpha & \cdots & 0 & 0 \cr \vdots & \vdots & \vdots & \cdots & \alpha & 0 \cr -0 & 0 & 0 & \cdots & -\alpha & \alpha +0 & 0 & 0 & \cdots & -\alpha & \alpha \end{bmatrix} \begin{bmatrix} \pi_0^* \cr \pi_1^* \cr \pi_2^* \cr \vdots \cr - \pi_{T+1}^* + \pi_{T+1}^* \end{bmatrix} $$ @@ -163,7 +163,7 @@ equation system. ## Harvesting insights from our matrix formulation We now have all of the ingredients we need to solve for $\pi$ as -a function of $\mu, \pi_0, \pi_0^*$. +a function of $\mu, \pi_0, \pi_0^*$. Combine equations {eq}`eq:eq1`and {eq}`eq:eq2` to get @@ -194,18 +194,18 @@ $$ We have thus solved for two of the key endogenous time series determined by our model, namely, the sequence $\pi^*$ -of expected inflation rates and the sequence $\pi$ of actual inflation rates. +of expected inflation rates and the sequence $\pi$ of actual inflation rates. Knowing these, we can then quickly calculate the associated sequence $p$ of the logarithm of the price level -from equation {eq}`eq:eqfiscth1`. +from equation {eq}`eq:eqfiscth1`. Let's fill in the details for this step. Since we now know $\mu$ it is easy to compute $m$. -Thus, notice that we can represent the equations +Thus, notice that we can represent the equations -$$ +$$ m_{t+1} = m_t + \mu_t , \quad t = 0, 1, \ldots, T $$ @@ -218,52 +218,52 @@ $$ 0 & -1 & 1 & \cdots & 0 & 0 \cr \vdots & \vdots & \vdots & \vdots & 0 & 0 \cr 0 & 0 & 0 & \cdots & 1 & 0 \cr -0 & 0 & 0 & \cdots & -1 & 1 +0 & 0 & 0 & \cdots & -1 & 1 \end{bmatrix} -\begin{bmatrix} +\begin{bmatrix} m_1 \cr m_2 \cr m_3 \cr \vdots \cr m_T \cr m_{T+1} \end{bmatrix} -= \begin{bmatrix} += \begin{bmatrix} \mu_0 \cr \mu_1 \cr \mu_2 \cr \vdots \cr \mu_{T-1} \cr \mu_T \end{bmatrix} -+ \begin{bmatrix} ++ \begin{bmatrix} m_0 \cr 0 \cr 0 \cr \vdots \cr 0 \cr 0 \end{bmatrix} $$ (eq:eq101_ad) -Multiplying both sides of equation {eq}`eq:eq101_ad` with the inverse of the matrix on the left will give +Multiplying both sides of equation {eq}`eq:eq101_ad` with the inverse of the matrix on the left will give $$ m_t = m_0 + \sum_{s=0}^{t-1} \mu_s, \quad t =1, \ldots, T+1 $$ (eq:mcum_ad) -Equation {eq}`eq:mcum_ad` shows that the log of the money supply at $t$ equals the log $m_0$ of the initial money supply +Equation {eq}`eq:mcum_ad` shows that the log of the money supply at $t$ equals the log $m_0$ of the initial money supply plus accumulation of rates of money growth between times $0$ and $t$. We can then compute $p_t$ for each $t$ from equation {eq}`eq:eqfiscth1`. We can write a compact formula for $p $ as -$$ +$$ p = m + \alpha \hat \pi^* $$ -where +where $$ \hat \pi^* = \begin{bmatrix} \pi_0^* \cr \pi_1^* \cr \pi_2^* \cr \vdots \cr - \pi_{T}^* + \pi_{T}^* \end{bmatrix}, $$ which is just $\pi^*$ with the last element dropped. - + ## Forecast errors and model computation -Our computations will verify that +Our computations will verify that $$ \hat \pi^* \neq \pi, @@ -271,12 +271,12 @@ $$ so that in general -$$ +$$ \pi_t^* \neq \pi_t, \quad t = 0, 1, \ldots , T $$ (eq:notre) This outcome is typical in models in which adaptive expectations hypothesis like equation {eq}`eq:adaptexpn` appear as a -component. +component. In {doc}`cagan_ree`, we studied a version of the model that replaces hypothesis {eq}`eq:adaptexpn` with a "perfect foresight" or "rational expectations" hypothesis. @@ -292,7 +292,7 @@ import matplotlib.pyplot as plt ``` ```{code-cell} ipython3 -Cagan_Adaptive = namedtuple("Cagan_Adaptive", +Cagan_Adaptive = namedtuple("Cagan_Adaptive", ["α", "m0", "Eπ0", "T", "λ"]) def create_cagan_adaptive_model(α = 5, m0 = 1, Eπ0 = 0.5, T=80, λ = 0.9): @@ -308,7 +308,7 @@ We solve the model and plot variables of interests using the following functions def solve_cagan_adaptive(model, μ_seq): " Solve the Cagan model in finite time. " α, m0, Eπ0, T, λ = model - + A = np.eye(T+2, T+2) - λ*np.eye(T+2, T+2, k=-1) B = np.eye(T+2, T+1, k=-1) C = -α*np.eye(T+1, T+2) + α*np.eye(T+1, T+2, k=1) @@ -337,11 +337,11 @@ def solve_cagan_adaptive(model, μ_seq): ```{code-cell} ipython3 def solve_and_plot(model, μ_seq): - + π_seq, Eπ_seq, m_seq, p_seq = solve_cagan_adaptive(model, μ_seq) - + T_seq = range(model.T+2) - + fig, ax = plt.subplots(5, 1, figsize=[5, 12], dpi=200) ax[0].plot(T_seq[:-1], μ_seq) ax[1].plot(T_seq[:-1], π_seq, label=r'$\pi_t$') @@ -349,7 +349,7 @@ def solve_and_plot(model, μ_seq): ax[2].plot(T_seq, m_seq - p_seq) ax[3].plot(T_seq, m_seq) ax[4].plot(T_seq, p_seq) - + y_labs = [r'$\mu$', r'$\pi$', r'$m - p$', r'$m$', r'$p$'] subplot_title = [r'Money supply growth', r'Inflation', r'Real balances', r'Money supply', r'Price level'] @@ -361,7 +361,7 @@ def solve_and_plot(model, μ_seq): ax[1].legend() plt.tight_layout() plt.show() - + return π_seq, Eπ_seq, m_seq, p_seq ``` @@ -385,7 +385,7 @@ $$ \end{aligned} $$ -By assuring that the coefficient on $\pi_t$ is less than one in absolute value, condition {eq}`eq:suffcond` assures stability of the dynamics of $\{\pi_t\}$ described by the last line of our string of deductions. +By assuring that the coefficient on $\pi_t$ is less than one in absolute value, condition {eq}`eq:suffcond` assures stability of the dynamics of $\{\pi_t\}$ described by the last line of our string of deductions. The reader is free to study outcomes in examples that violate condition {eq}`eq:suffcond`. @@ -402,7 +402,7 @@ Now we'll turn to some experiments. We'll study a situation in which the rate of growth of the money supply is $\mu_0$ from $t=0$ to $t= T_1$ and then permanently falls to $\mu^*$ at $t=T_1$. -Thus, let $T_1 \in (0, T)$. +Thus, let $T_1 \in (0, T)$. So where $\mu_0 > \mu^*$, we assume that @@ -438,11 +438,11 @@ We invite you to explain to yourself the source of this overshooting and why it ### Experiment 2 Now we'll do a different experiment, namely, a gradual stabilization in which the rate of growth of the money supply smoothly -declines from a high value to a persistently low value. +declines from a high value to a persistently low value. While price level inflation eventually falls, it falls more slowly than the driving force that ultimately causes it to fall, namely, the falling rate of growth of the money supply. -The sluggish fall in inflation is explained by how anticipated inflation $\pi_t^*$ persistently exceeds actual inflation $\pi_t$ during the transition from a high inflation to a low inflation situation. +The sluggish fall in inflation is explained by how anticipated inflation $\pi_t^*$ persistently exceeds actual inflation $\pi_t$ during the transition from a high inflation to a low inflation situation. ```{code-cell} ipython3 # parameters @@ -454,3 +454,287 @@ The sluggish fall in inflation is explained by how anticipated inflation $\pi_t # solve and plot π_seq_2, Eπ_seq_2, m_seq_2, p_seq_2 = solve_and_plot(md, μ_seq_2) ``` + +## Exercises + +```{exercise} +:label: ca_ex1 + +**Sensitivity of overshooting to the learning speed $\lambda$.** + +For Experiment 1 (sudden stabilization at $T_1 = 60$ from $\mu_0 = 0.5$ to $\mu^* = 0$), solve the model for $\lambda \in \{0.86,\, 0.90,\, 0.95,\, 0.98\}$ and, on a single graph, plot the actual inflation rate $\pi_t$ for each value. + +a. How do the sign and speed of post-stabilization convergence change as $\lambda$ varies within the stable region? + +b. For each $\lambda$, print $\rho$ and the peak absolute value of $\pi_t$ for $t \geq T_1$. +``` + +```{solution-start} ca_ex1 +:class: dropdown +``` + +```{code-cell} ipython3 +T1 = 60 +μ0 = 0.5 +μ_star = 0.0 +λ_vals = [0.86, 0.90, 0.95, 0.98] + +fig, ax = plt.subplots(figsize=(9, 4)) +for λ in λ_vals: + m = create_cagan_adaptive_model(λ=λ) + μ_seq = np.append(μ0 * np.ones(T1), μ_star * np.ones(m.T + 1 - T1)) + π_seq, _, _, _ = solve_cagan_adaptive(m, μ_seq) + ax.plot(range(m.T + 1), π_seq, label=f'λ = {λ}') + +ax.axvline(T1, linestyle='--', color='black', lw=1, label='Stabilization $T_1$') +ax.axhline(μ_star, linestyle=':', color='gray', lw=0.8) +ax.set_xlabel('$t$') +ax.set_ylabel(r'$\pi_t$') +ax.set_title('Inflation paths for different λ (sudden stabilization)') +ax.legend() +plt.show() + +print(f'{"λ":>6} | {"ρ":>10} | {"|ρ|<1":>8} | {"peak |π| after T1":>20}') +print('-' * 56) +for λ in λ_vals: + m = create_cagan_adaptive_model(λ=λ) + μ_seq = np.append(μ0 * np.ones(T1), μ_star * np.ones(m.T + 1 - T1)) + π_seq, _, _, _ = solve_cagan_adaptive(m, μ_seq) + ρ = (λ - m.α * (1 - λ)) / (1 - m.α * (1 - λ)) + peak = np.max(np.abs(π_seq[T1:])) + print(f'{λ:>6.2f} | {ρ:>10.4f} | {str(abs(ρ) < 1):>8} | {peak:>20.4f}') +``` + +All four values satisfy the stability condition $|\rho| < 1$ for the default $\alpha = 5$ and have $\rho > 0$. + +The case $\lambda = 0.86$ has the largest initial overshoot among these four values and then converges the fastest. + +As $\lambda$ moves closer to one, expectations become more inertial, so the post-stabilization response decays more slowly but starts from a smaller jump. + +For $\alpha = 5$, an oscillatory stable response would require $0.8 < \lambda < 5/6$. + +```{solution-end} +``` + +```{exercise} +:label: ca_ex2 + +**Systematic forecast errors under adaptive expectations.** + +The lecture notes that $\pi_t^* \neq \pi_t$ in general under adaptive +expectations, in contrast to a rational-expectations equilibrium. + +For the default model (`md`) and both experiments: + +a. Compute and plot the forecast error $e_t = \pi_t^* - \pi_t$ for + $t = 0, 1, \ldots, T$. + +b. For each experiment, determine whether $e_t$ is systematically positive or + negative during the disinflation and explain why this systematic bias could + not survive under rational expectations. + +(Recall that `Eπ_seq` returned by `solve_cagan_adaptive` has $T+2$ elements +while `π_seq` has $T+1$; use `Eπ_seq[:-1]` to align them.) +``` + +```{solution-start} ca_ex2 +:class: dropdown +``` + +```{code-cell} ipython3 +T1 = 60 +μ0 = 0.5 +μ_star = 0.0 + +# Experiment 1 sequences +μ_seq_1 = np.append(μ0 * np.ones(T1), μ_star * np.ones(md.T + 1 - T1)) +π1, Eπ1, _, _ = solve_cagan_adaptive(md, μ_seq_1) + +# Experiment 2 sequences +ϕ = 0.9 +μ_seq_2 = np.array([ϕ**t * μ0 + (1 - ϕ**t) * μ_star for t in range(md.T)]) +μ_seq_2 = np.append(μ_seq_2, μ_star) +π2, Eπ2, _, _ = solve_cagan_adaptive(md, μ_seq_2) + +t_seq = np.arange(md.T + 1) +e1 = Eπ1[:-1] - π1 # forecast error, length T+1 +e2 = Eπ2[:-1] - π2 + +fig, axes = plt.subplots(1, 2, figsize=(11, 4)) +axes[0].plot(t_seq, e1) +axes[0].axhline(0, color='black', lw=0.8, linestyle='--') +axes[0].axvline(T1, color='gray', lw=0.8, linestyle=':') +axes[0].set_title('Forecast error: Experiment 1 (sudden)') +axes[0].set_xlabel('$t$') +axes[0].set_ylabel(r'$\pi_t^* - \pi_t$') + +axes[1].plot(t_seq, e2, color='C1') +axes[1].axhline(0, color='black', lw=0.8, linestyle='--') +axes[1].set_title('Forecast error: Experiment 2 (gradual)') +axes[1].set_xlabel('$t$') +axes[1].set_ylabel(r'$\pi_t^* - \pi_t$') + +plt.tight_layout() +plt.show() + +print(f'Exp 1: mean forecast error t < T1: {e1[:T1].mean():.4f}') +print(f'Exp 1: mean forecast error t >= T1: {e1[T1:].mean():.4f}') +print(f'Exp 2: mean forecast error overall: {e2.mean():.4f}') +``` + +During disinflation, actual inflation falls *below* expected inflation, so +$e_t = \pi_t^* - \pi_t > 0$ throughout the transition, so the public +systematically **over-predicts** inflation. + +Under rational expectations this persistent one-sided bias would be immediately arbitraged away as agents adjust their forecasting rule until $e_t$ has mean zero. + +```{solution-end} +``` + +```{exercise} +:label: ca_ex3 + +**Post-stabilization convergence rate.** + +The lecture derives that, after the money-growth rate has been permanently set to +$\mu^*$, the actual inflation rate $\pi_t$ decays geometrically: + +$$ +\pi_{t+1} = \rho\, \pi_t, \qquad +\rho \equiv \frac{\lambda - \alpha(1-\lambda)}{1 - \alpha(1-\lambda)}. +$$ + +Using Experiment 1 and the default model `md`: + +a. Compute $\rho$ analytically from the model parameters and verify that + $|\rho| < 1$ (the stability condition {eq}`eq:suffcond`). + +b. From the solved path `π_seq`, compute the empirical ratios + $\pi_{t+1}/\pi_t$ for $t = T_1 + 1, \ldots, T_1 + 10$ and compare them + to $\rho$. + +c. Plot $\log|\pi_t|$ against $t$ for $t \geq T_1$ and verify that it is + linear with slope $\log|\rho|$. +``` + +```{solution-start} ca_ex3 +:class: dropdown +``` + +```{code-cell} ipython3 +T1 = 60 +μ0 = 0.5 +μ_star = 0.0 + +α, λ = md.α, md.λ +ρ = (λ - α * (1 - λ)) / (1 - α * (1 - λ)) +print(f'α = {α}, λ = {λ}') +print(f'ρ = {ρ:.6f} (|ρ| < 1: {abs(ρ) < 1})') + +μ_seq = np.append(μ0 * np.ones(T1), μ_star * np.ones(md.T + 1 - T1)) +π_seq, _, _, _ = solve_cagan_adaptive(md, μ_seq) + +# Part b: empirical successive ratios +print(f'\n{"t":>5} | {"π_t":>12} | {"π_{t+1}/π_t":>14} | {"ρ":>8}') +print('-' * 46) +for t in range(T1, T1 + 10): + ratio = π_seq[t + 1] / π_seq[t] + print(f'{t:>5} | {π_seq[t]:>12.6f} | {ratio:>14.6f} | {ρ:>8.6f}') +``` + +```{code-cell} ipython3 +# Part c: log|π_t| is linear after T1 +t_post = np.arange(T1, md.T + 1) +log_π = np.log(np.abs(π_seq[T1:])) + +fig, ax = plt.subplots() +ax.plot(t_post, log_π, label=r'$\log|\pi_t|$') +# overlay the theoretical slope +slope_theory = np.log(abs(ρ)) +ax.plot(t_post, + log_π[0] + slope_theory * (t_post - T1), + linestyle='--', label=f'slope = log|ρ| = {slope_theory:.4f}') +ax.set_xlabel('$t$') +ax.set_ylabel(r'$\log|\pi_t|$') +ax.set_title('Geometric decay of inflation after stabilization') +ax.legend() +plt.show() +``` + +The empirical ratios converge to $\rho = 0.8$ immediately after $T_1$, confirming +the first-order difference equation derived analytically. + +The log plot is exactly linear with the theoretical slope, reflecting the exact geometric convergence +$\pi_t = \rho^{t-T_1} \pi_{T_1}$ for $t \geq T_1$. + +```{solution-end} +``` + +```{exercise} +:label: ca_ex4 + +**Fast vs slow learning under gradual stabilization.** + +Experiment 2 uses a gradual decline in money growth +$\mu_t = \phi^t \mu_0 + (1-\phi^t)\mu^*$ with $\phi = 0.9$. + +a. For the same gradual $\mu$ path, compare the inflation $\pi_t$ and expected inflation $\pi_t^*$ paths for two stable cases: + + * **Faster adjustment**: $\lambda = 0.86$ + * **Slower adjustment**: $\lambda = 0.95$ + + Plot $\pi_t$, $\pi_t^*$, and $\mu_t$ for each case on side-by-side graphs. + +b. For each case, compute the mean absolute forecast error $\bar{e} = \frac{1}{T+1}\sum_{t=0}^T |\pi_t^* - \pi_t|$. + +c. Explain why the faster-adjustment case can move below the money-growth path while the slower-adjustment case displays more persistent forecast errors. +``` + +```{solution-start} ca_ex4 +:class: dropdown +``` + +```{code-cell} ipython3 +μ0 = 0.5 +μ_star = 0.0 +ϕ = 0.9 + +λ_cases = {'Faster adjustment (λ=0.86)': 0.86, + 'Slower adjustment (λ=0.95)': 0.95} + +fig, axes = plt.subplots(1, 2, figsize=(12, 4)) + +for ax, (label, λ) in zip(axes, λ_cases.items()): + m = create_cagan_adaptive_model(λ=λ) + μ_seq = np.array([ϕ**t * μ0 + (1 - ϕ**t) * μ_star for t in range(m.T)]) + μ_seq = np.append(μ_seq, μ_star) + π_seq, Eπ_seq, _, _ = solve_cagan_adaptive(m, μ_seq) + + t_seq = np.arange(m.T + 1) + ax.plot(t_seq, μ_seq, label=r'$\mu_t$', linestyle=':', color='black') + ax.plot(t_seq, π_seq, label=r'$\pi_t$', lw=1.5) + ax.plot(t_seq, Eπ_seq[:-1], label=r'$\pi_t^*$', linestyle='--', lw=1.5) + ax.set_xlabel('$t$') + ax.set_title(label) + ax.legend(fontsize=8) + +plt.tight_layout() +plt.show() + +print(f'{"Case":>30} | {"Mean |forecast error|":>22}') +print('-' * 56) +for label, λ in λ_cases.items(): + m = create_cagan_adaptive_model(λ=λ) + μ_seq = np.array([ϕ**t * μ0 + (1 - ϕ**t) * μ_star for t in range(m.T)]) + μ_seq = np.append(μ_seq, μ_star) + π_seq, Eπ_seq, _, _ = solve_cagan_adaptive(m, μ_seq) + mae = np.mean(np.abs(Eπ_seq[:-1] - π_seq)) + print(f'{label:>30} | {mae:>22.6f}') +``` + +With faster adjustment, expectations revise downward more aggressively and inflation can move below the money-growth path during the transition. + +With slower adjustment, expectations remain elevated for longer and the forecast errors are larger and slower to disappear. + +```{solution-end} +``` diff --git a/lectures/cagan_ree.md b/lectures/cagan_ree.md index f0274b56a..ea0b1ff16 100644 --- a/lectures/cagan_ree.md +++ b/lectures/cagan_ree.md @@ -18,16 +18,16 @@ kernelspec: We'll use linear algebra first to explain and then do some experiments with a "monetarist theory of price levels". -Economists call it a "monetary" or "monetarist" theory of price levels because effects on price levels occur via a central bank's decisions to print money supply. +Economists call it a "monetary" or "monetarist" theory of price levels because effects on price levels occur via a central bank's decisions to print money supply. * a goverment's fiscal policies determine whether its _expenditures_ exceed its _tax collections_ * if its expenditures exceed its tax collections, the government can instruct the central bank to cover the difference by _printing money_ * that leads to effects on the price level as price level path adjusts to equate the supply of money to the demand for money -Such a theory of price levels was described by Thomas Sargent and Neil Wallace in chapter 5 of -{cite}`sargent2013rational`, which reprints a 1981 Federal Reserve Bank of Minneapolis article entitled "Unpleasant Monetarist Arithmetic". +Such a theory of price levels was described by Thomas Sargent and Neil Wallace in chapter 5 of +{cite}`sargent2013rational`, which reprints a 1981 Federal Reserve Bank of Minneapolis article entitled "Unpleasant Monetarist Arithmetic". -Sometimes this theory is also called a "fiscal theory of price levels" to emphasize the importance of fiscal deficits in shaping changes in the money supply. +Sometimes this theory is also called a "fiscal theory of price levels" to emphasize the importance of fiscal deficits in shaping changes in the money supply. The theory has been extended, criticized, and applied by John Cochrane {cite}`cochrane2023fiscal`. @@ -39,25 +39,25 @@ Elemental forces at work in the fiscal theory of the price level help to underst According to this theory, when the government persistently spends more than it collects in taxes and prints money to finance the shortfall (the "shortfall" is called the "government deficit"), it puts upward pressure on the price level and generates persistent inflation. -The "monetarist" or "fiscal theory of price levels" asserts that +The "monetarist" or "fiscal theory of price levels" asserts that * to _start_ a persistent inflation the government begins persistently to run a money-financed government deficit * to _stop_ a persistent inflation the government stops persistently running a money-financed government deficit -The model in this lecture is a "rational expectations" (or "perfect foresight") version of a model that Philip Cagan {cite}`Cagan` used to study the monetary dynamics of hyperinflations. +The model in this lecture is a "rational expectations" (or "perfect foresight") version of a model that Philip Cagan {cite}`Cagan` used to study the monetary dynamics of hyperinflations. While Cagan didn't use that "rational expectations" version of the model, Thomas Sargent {cite}`sargent1982ends` did when he studied the Ends of Four Big Inflations in Europe after World War I. -* this lecture {doc}`fiscal theory of the price level with adaptive expectations ` describes a version of the model that does not impose "rational expectations" but instead uses +* this lecture {doc}`fiscal theory of the price level with adaptive expectations ` describes a version of the model that does not impose "rational expectations" but instead uses what Cagan and his teacher Milton Friedman called "adaptive expectations" * a reader of both lectures will notice that the algebra is less complicated in the present rational expectations version of the model - * the difference in algebra complications can be traced to the following source: the adaptive expectations version of the model has more endogenous variables and more free parameters + * the difference in algebra complications can be traced to the following source: the adaptive expectations version of the model has more endogenous variables and more free parameters Some of our quantitative experiments with the rational expectations version of the model are designed to illustrate how the fiscal theory explains the abrupt end of those big inflations. -In those experiments, we'll encounter an instance of a "velocity dividend" that has sometimes accompanied successful inflation stabilization programs. +In those experiments, we'll encounter an instance of a "velocity dividend" that has sometimes accompanied successful inflation stabilization programs. To facilitate using linear matrix algebra as our main mathematical tool, we'll use a finite horizon version of the model. @@ -76,8 +76,8 @@ The model consists of * an equilibrium condition that equates the demand for money to the supply * a "perfect foresight" assumption that the public's expected rate of inflation equals the actual rate of inflation. - -To represent the model formally, let + +To represent the model formally, let * $ m_t $ be the log of the supply of nominal money balances; * $\mu_t = m_{t+1} - m_t $ be the net rate of growth of nominal balances; @@ -88,8 +88,8 @@ To represent the model formally, let * $\pi_{T+1}^*$ the terminal rate of inflation between times $T$ and $T+1$. The demand for real balances $\exp\left(m_t^d - p_t\right)$ is governed by the following version of the Cagan demand function - -$$ + +$$ m_t^d - p_t = -\alpha \pi_t^* \: , \: \alpha > 0 ; \quad t = 0, 1, \ldots, T . $$ (eq:caganmd) @@ -101,11 +101,11 @@ problem. This lets us set -$$ -\pi_t^* = \pi_t , % \forall t +$$ +\pi_t^* = \pi_t , % \forall t $$ (eq:ree) -while equating demand for money to supply lets us set $m_t^d = m_t$ for all $t \geq 0$. +while equating demand for money to supply lets us set $m_t^d = m_t$ for all $t \geq 0$. The preceding equations then imply @@ -124,7 +124,7 @@ which we rewrite as a forward-looking first-order linear difference equation in $\pi_s$ with $\mu_s$ as a "forcing variable": $$ -\pi_t = \frac{\alpha}{1+\alpha} \pi_{t+1} + \frac{1}{1+\alpha} \mu_t , \quad t= 0, 1, \ldots , T +\pi_t = \frac{\alpha}{1+\alpha} \pi_{t+1} + \frac{1}{1+\alpha} \mu_t , \quad t= 0, 1, \ldots , T $$ where $ 0< \frac{\alpha}{1+\alpha} <1 $. @@ -144,12 +144,12 @@ $$ \vdots & \vdots & \vdots & \vdots & \vdots & -\delta & 0 \cr 0 & 0 & 0 & 0 & \cdots & 1 & -\delta \cr 0 & 0 & 0 & 0 & \cdots & 0 & 1 \end{bmatrix} -\begin{bmatrix} \pi_0 \cr \pi_1 \cr \pi_2 \cr \vdots \cr \pi_{T-1} \cr \pi_T -\end{bmatrix} -= (1 - \delta) \begin{bmatrix} +\begin{bmatrix} \pi_0 \cr \pi_1 \cr \pi_2 \cr \vdots \cr \pi_{T-1} \cr \pi_T +\end{bmatrix} += (1 - \delta) \begin{bmatrix} \mu_0 \cr \mu_1 \cr \mu_2 \cr \vdots \cr \mu_{T-1} \cr \mu_T \end{bmatrix} -+ \begin{bmatrix} ++ \begin{bmatrix} 0 \cr 0 \cr 0 \cr \vdots \cr 0 \cr \delta \pi_{T+1}^* \end{bmatrix} $$ (eq:pieq) @@ -157,8 +157,8 @@ $$ (eq:pieq) By multiplying both sides of equation {eq}`eq:pieq` by the inverse of the matrix on the left side, we can calculate $$ -\pi \equiv \begin{bmatrix} \pi_0 \cr \pi_1 \cr \pi_2 \cr \vdots \cr \pi_{T-1} \cr \pi_T -\end{bmatrix} +\pi \equiv \begin{bmatrix} \pi_0 \cr \pi_1 \cr \pi_2 \cr \vdots \cr \pi_{T-1} \cr \pi_T +\end{bmatrix} $$ It turns out that @@ -167,9 +167,9 @@ $$ \pi_t = (1-\delta) \sum_{s=t}^T \delta^{s-t} \mu_s + \delta^{T+1-t} \pi_{T+1}^* $$ (eq:fisctheory1) -We can represent the equations +We can represent the equations -$$ +$$ m_{t+1} = m_t + \mu_t , \quad t = 0, 1, \ldots, T $$ @@ -182,20 +182,20 @@ $$ 0 & -1 & 1 & \cdots & 0 & 0 \cr \vdots & \vdots & \vdots & \vdots & 0 & 0 \cr 0 & 0 & 0 & \cdots & 1 & 0 \cr -0 & 0 & 0 & \cdots & -1 & 1 +0 & 0 & 0 & \cdots & -1 & 1 \end{bmatrix} -\begin{bmatrix} +\begin{bmatrix} m_1 \cr m_2 \cr m_3 \cr \vdots \cr m_T \cr m_{T+1} \end{bmatrix} -= \begin{bmatrix} += \begin{bmatrix} \mu_0 \cr \mu_1 \cr \mu_2 \cr \vdots \cr \mu_{T-1} \cr \mu_T \end{bmatrix} -+ \begin{bmatrix} ++ \begin{bmatrix} m_0 \cr 0 \cr 0 \cr \vdots \cr 0 \cr 0 \end{bmatrix} $$ (eq:eq101) -Multiplying both sides of equation {eq}`eq:eq101` with the inverse of the matrix on the left will give +Multiplying both sides of equation {eq}`eq:eq101` with the inverse of the matrix on the left will give $$ m_t = m_0 + \sum_{s=0}^{t-1} \mu_s, \quad t =1, \ldots, T+1 @@ -210,7 +210,7 @@ To determine the continuation inflation rate $\pi_{T+1}^*$ we shall proceed by a version of equation {eq}`eq:fisctheory1` at time $t = T+1$: $$ -\pi_t = (1-\delta) \sum_{s=t}^\infty \delta^{s-t} \mu_s , +\pi_t = (1-\delta) \sum_{s=t}^\infty \delta^{s-t} \mu_s , $$ (eq:fisctheory2) and by also assuming the following continuation path for $\mu_t$ beyond $T$: @@ -221,7 +221,7 @@ $$ Plugging the preceding equation into equation {eq}`eq:fisctheory2` at $t = T+1$ and rearranging we can deduce that -$$ +$$ \pi_{T+1}^* = \frac{1 - \delta}{1 - \delta \gamma^*} \gamma^* \mu_T $$ (eq:piterm) @@ -242,7 +242,7 @@ First, we store parameters in a `namedtuple`: ```{code-cell} ipython3 # Create the rational expectation version of Cagan model in finite time -CaganREE = namedtuple("CaganREE", +CaganREE = namedtuple("CaganREE", ["m0", # initial money supply "μ_seq", # sequence of rate of growth "α", # sensitivity parameter @@ -260,9 +260,9 @@ Now we can solve the model to compute $\pi_t$, $m_t$ and $p_t$ for $t =1, \ldots ```{code-cell} ipython3 def solve(model, T): - m0, π_end, μ_seq, α, δ = (model.m0, model.π_end, + m0, π_end, μ_seq, α, δ = (model.m0, model.π_end, model.μ_seq, model.α, model.δ) - + # Create matrix representation above A1 = np.eye(T+1, T+1) - δ * np.eye(T+1, T+1, k=1) A2 = np.eye(T+1, T+1) - np.eye(T+1, T+1, k=-1) @@ -290,13 +290,13 @@ In devising these experiments, we'll make assumptions about $\{\mu_t\}$ that are We describe several such experiments. -In all of them, +In all of them, -$$ +$$ \mu_t = \mu^* , \quad t \geq T_1 $$ -so that, in terms of our notation and formula for $\pi_{T+1}^*$ above, $\gamma^* = 1$. +so that, in terms of our notation and formula for $\pi_{T+1}^*$ above, $\gamma^* = 1$. #### Experiment 1: Foreseen sudden stabilization @@ -305,7 +305,7 @@ In this experiment, we'll study how, when $\alpha >0$, a foreseen inflation stab We'll study a situation in which the rate of growth of the money supply is $\mu_0$ from $t=0$ to $t= T_1$ and then permanently falls to $\mu^*$ at $t=T_1$. -Thus, let $T_1 \in (0, T)$. +Thus, let $T_1 \in (0, T)$. So where $\mu_0 > \mu^*$, we assume that @@ -316,7 +316,7 @@ $$ \end{cases} $$ -We'll start by executing a version of our "experiment 1" in which the government implements a _foreseen_ sudden permanent reduction in the rate of money creation at time $T_1$. +We'll start by executing a version of our "experiment 1" in which the government implements a _foreseen_ sudden permanent reduction in the rate of money creation at time $T_1$. Let's experiment with the following parameters @@ -352,15 +352,15 @@ plot_sequences(sequences, (r'$\mu$', r'$\pi$', r'$m - p$', r'$m$', r'$p$')) ``` The plot of the money growth rate $\mu_t$ in the top level panel portrays -a sudden reduction from $.5$ to $0$ at time $T_1 = 60$. +a sudden reduction from $.5$ to $0$ at time $T_1 = 60$. This brings about a gradual reduction of the inflation rate $\pi_t$ that precedes the money supply growth rate reduction at time $T_1$. -Notice how the inflation rate declines smoothly (i.e., continuously) to $0$ at $T_1$ -- +Notice how the inflation rate declines smoothly (i.e., continuously) to $0$ at $T_1$ -- unlike the money growth rate, it does not suddenly "jump" downward at $T_1$. -This is because the reduction in $\mu$ at $T_1$ has been foreseen from the start. +This is because the reduction in $\mu$ at $T_1$ has been foreseen from the start. While the log money supply portrayed in the bottom panel has a kink at $T_1$, the log price level does not -- it is "smooth" -- once again a consequence of the fact that the reduction in $\mu$ has been foreseen. @@ -379,15 +379,15 @@ $$ (eq:pformula2) or, by using equation {eq}`eq:fisctheory1`, -$$ -p_t = m_t + \alpha \left[ (1-\delta) \sum_{s=t}^T \delta^{s-t} \mu_s + \delta^{T+1-t} \pi_{T+1}^* \right] +$$ +p_t = m_t + \alpha \left[ (1-\delta) \sum_{s=t}^T \delta^{s-t} \mu_s + \delta^{T+1-t} \pi_{T+1}^* \right] $$ (eq:pfiscaltheory2) -In our next experiment, we'll study a "surprise" permanent change in the money growth that beforehand -was completely unanticipated. +In our next experiment, we'll study a "surprise" permanent change in the money growth that beforehand +was completely unanticipated. At time $T_1$ when the "surprise" money growth rate change occurs, to satisfy -equation {eq}`eq:pformula2`, the log of real balances jumps +equation {eq}`eq:pformula2`, the log of real balances jumps _upward_ as $\pi_t$ jumps _downward_. But in order for $m_t - p_t$ to jump, which variable jumps, $m_{T_1}$ or $p_{T_1}$? @@ -400,8 +400,8 @@ What jumps at $T_1$? Is it $p_{T_1}$ or $m_{T_1}$? -If we insist that the money supply $m_{T_1}$ is locked at its value $m_{T_1}^1$ inherited from the past, then formula {eq}`eq:pformula2` implies that the price level jumps downward at time $T_1$, to coincide with the downward jump in -$\pi_{T_1}$ +If we insist that the money supply $m_{T_1}$ is locked at its value $m_{T_1}^1$ inherited from the past, then formula {eq}`eq:pformula2` implies that the price level jumps downward at time $T_1$, to coincide with the downward jump in +$\pi_{T_1}$ An alternative assumption about the money supply level is that as part of the "inflation stabilization", the government resets $m_{T_1}$ according to @@ -410,7 +410,7 @@ $$ m_{T_1}^2 - m_{T_1}^1 = \alpha (\pi_{T_1}^1 - \pi_{T_1}^2), $$ (eq:eqnmoneyjump) -which describes how the government could reset the money supply at $T_1$ in response to the jump in expected inflation associated with monetary stabilization. +which describes how the government could reset the money supply at $T_1$ in response to the jump in expected inflation associated with monetary stabilization. Doing this would let the price level be continuous at $T_1$. @@ -425,7 +425,7 @@ We have noted that with a constant expected forward sequence $\mu_s = \bar \mu$ A consequence is that at $T_1$, either $m$ or $p$ must "jump" at $T_1$. -We'll study both cases. +We'll study both cases. #### $m_{T_{1}}$ does not jump. @@ -441,10 +441,10 @@ Simply glue the sequences $t\leq T_1$ and $t > T_1$. We reset $m_{T_{1}}$ so that $p_{T_{1}}=\left(m_{T_{1}-1}+\mu_{0}\right)+\alpha\mu_{0}$, with $\pi_{T_{1}}=\mu^{*}$. -Then, +Then, -$$ -m_{T_{1}}=p_{T_{1}}-\alpha\pi_{T_{1}}=\left(m_{T_{1}-1}+\mu_{0}\right)+\alpha\left(\mu_{0}-\mu^{*}\right) +$$ +m_{T_{1}}=p_{T_{1}}-\alpha\pi_{T_{1}}=\left(m_{T_{1}-1}+\mu_{0}\right)+\alpha\left(\mu_{0}-\mu^{*}\right) $$ We then compute for the remaining $T-T_{1}$ periods with $\mu_{s}=\mu^{*},\forall s\geq T_{1}$ and the initial condition $m_{T_{1}}$ from above. @@ -455,29 +455,30 @@ We are now technically equipped to discuss our next experiment. This experiment deviates a little bit from a pure version of our "perfect foresight" assumption by assuming that a sudden permanent reduction in $\mu_t$ like that -analyzed in experiment 1 is completely unanticipated. +analyzed in experiment 1 is completely unanticipated. Such a completely unanticipated shock is popularly known as an "MIT shock". -The mental experiment involves switching at time $T_1$ from an initial "continuation path" for $\{\mu_t, \pi_t\} $ to another path that involves a permanently lower inflation rate. +The mental experiment involves switching at time $T_1$ from an initial "continuation path" for $\{\mu_t, \pi_t\} $ to another path that involves a permanently lower inflation rate. -**Initial Path:** $\mu_t = \mu_0$ for all $t \geq 0$. So this path is for $\{\mu_t\}_{t=0}^\infty$; the associated -path for $\pi_t$ has $\pi_t = \mu_0$. +**Initial Path:** $\mu_t = \mu_0$ for all $t \geq 0$. + +This path is for $\{\mu_t\}_{t=0}^\infty$; the associated path for $\pi_t$ has $\pi_t = \mu_0$. **Revised Continuation Path** Where $ \mu_0 > \mu^*$, we construct a continuation path $\{\mu_s\}_{s=T_1}^\infty$ -by setting $\mu_s = \mu^*$ for all $s \geq T_1$. The perfect foresight continuation path for -$\pi$ is $\pi_s = \mu^*$ +by setting $\mu_s = \mu^*$ for all $s \geq T_1$. The perfect foresight continuation path for +$\pi$ is $\pi_s = \mu^*$ To capture a "completely unanticipated permanent shock to the $\{\mu_t\}$ process at time $T_1$, we simply glue the $\mu_t, \pi_t$ that emerges under path 2 for $t \geq T_1$ to the $\mu_t, \pi_t$ path that had emerged under path 1 for $ t=0, \ldots, T_1 -1$. -We can do the MIT shock calculations mostly by hand. +We can do the MIT shock calculations mostly by hand. Thus, for path 1, $\pi_t = \mu_0 $ for all $t \in [0, T_1-1]$, while for path 2, -$\mu_s = \mu^*$ for all $s \geq T_1$. +$\mu_s = \mu^*$ for all $s \geq T_1$. -We now move on to experiment 2, our "MIT shock", completely unforeseen +We now move on to experiment 2, our "MIT shock", completely unforeseen sudden stabilization. We set this up so that the $\{\mu_t\}$ sequences that describe the sudden stabilization @@ -495,7 +496,7 @@ cm1 = create_cagan_model(μ_seq=μ_seq_2_path1) # continuation path μ_seq_2_cont = μ_star * np.ones(T-T1) -cm2 = create_cagan_model(m0=m_seq_2_path1[T1+1], +cm2 = create_cagan_model(m0=m_seq_2_path1[T1+1], μ_seq=μ_seq_2_cont) π_seq_2_cont, m_seq_2_cont1, p_seq_2_cont1 = solve(cm2, T-1-T1) @@ -503,11 +504,11 @@ cm2 = create_cagan_model(m0=m_seq_2_path1[T1+1], # regime 1 - simply glue π_seq, μ_seq μ_seq_2 = np.concatenate((μ_seq_2_path1[:T1+1], μ_seq_2_cont)) -π_seq_2 = np.concatenate((π_seq_2_path1[:T1+1], +π_seq_2 = np.concatenate((π_seq_2_path1[:T1+1], π_seq_2_cont)) -m_seq_2_regime1 = np.concatenate((m_seq_2_path1[:T1+1], +m_seq_2_regime1 = np.concatenate((m_seq_2_path1[:T1+1], m_seq_2_cont1)) -p_seq_2_regime1 = np.concatenate((p_seq_2_path1[:T1+1], +p_seq_2_regime1 = np.concatenate((p_seq_2_path1[:T1+1], p_seq_2_cont1)) # regime 2 - reset m_T1 @@ -516,7 +517,7 @@ m_T1 = (m_seq_2_path1[T1] + μ0) + cm2.α*(μ0 - μ_star) cm3 = create_cagan_model(m0=m_T1, μ_seq=μ_seq_2_cont) π_seq_2_cont2, m_seq_2_cont2, p_seq_2_cont2 = solve(cm3, T-1-T1) -m_seq_2_regime2 = np.concatenate((m_seq_2_path1[:T1+1], +m_seq_2_regime2 = np.concatenate((m_seq_2_path1[:T1+1], m_seq_2_cont2)) p_seq_2_regime2 = np.concatenate((p_seq_2_path1[:T1+1], p_seq_2_cont2)) @@ -534,13 +535,13 @@ fig, ax = plt.subplots(5, 1, figsize=(5, 12), dpi=200) plot_configs = [ {'data': [(T_seq[:-1], μ_seq_2)], 'ylabel': r'$\mu$'}, {'data': [(T_seq, π_seq_2)], 'ylabel': r'$\pi$'}, - {'data': [(T_seq, m_seq_2_regime1 - p_seq_2_regime1)], + {'data': [(T_seq, m_seq_2_regime1 - p_seq_2_regime1)], 'ylabel': r'$m - p$'}, - {'data': [(T_seq, m_seq_2_regime1, 'Smooth $m_{T_1}$'), - (T_seq, m_seq_2_regime2, 'Jumpy $m_{T_1}$')], + {'data': [(T_seq, m_seq_2_regime1, 'Smooth $m_{T_1}$'), + (T_seq, m_seq_2_regime2, 'Jumpy $m_{T_1}$')], 'ylabel': r'$m$'}, - {'data': [(T_seq, p_seq_2_regime1, 'Smooth $p_{T_1}$'), - (T_seq, p_seq_2_regime2, 'Jumpy $p_{T_1}$')], + {'data': [(T_seq, p_seq_2_regime1, 'Smooth $p_{T_1}$'), + (T_seq, p_seq_2_regime2, 'Jumpy $p_{T_1}$')], 'ylabel': r'$p$'} ] @@ -557,29 +558,29 @@ def experiment_plot(plot_configs, ax): axi.set_xlabel(r'$t$') plt.tight_layout() plt.show() - + experiment_plot(plot_configs, ax) ``` We invite you to compare these graphs with corresponding ones for the foreseen stabilization analyzed in experiment 1 above. -Note how the inflation graph in the second panel is now identical to the +Note how the inflation graph in the second panel is now identical to the money growth graph in the top panel, and how now the log of real balances portrayed in the third panel jumps upward at time $T_1$. The bottom two panels plot $m$ and $p$ under two possible ways that $m_{T_1}$ might adjust -as required by the upward jump in $m - p$ at $T_1$. +as required by the upward jump in $m - p$ at $T_1$. * the orange line lets $m_{T_1}$ jump upward in order to make sure that the log price level $p_{T_1}$ does not fall. * the blue line lets $p_{T_1}$ fall while stopping the money supply from jumping. - + Here is a way to interpret what the government is doing when the orange line policy is in place. The government prints money to finance expenditure with the "velocity dividend" that it reaps from the increased demand for real balances brought about by the permanent decrease in the rate of growth of the money supply. The next code generates a multi-panel graph that includes outcomes of both experiments 1 and 2. -That allows us to assess how important it is to understand whether the sudden permanent drop in $\mu_t$ at $t=T_1$ is fully unanticipated, as in experiment 1, or completely +That allows us to assess how important it is to understand whether the sudden permanent drop in $\mu_t$ at $t=T_1$ is fully anticipated, as in experiment 1, or completely unanticipated, as in experiment 2. ```{code-cell} ipython3 @@ -590,16 +591,16 @@ fig, ax = plt.subplots(5, figsize=(5, 12), dpi=200) plot_configs = [ {'data': [(T_seq[:-1], μ_seq_2)], 'ylabel': r'$\mu$'}, - {'data': [(T_seq, π_seq_2, 'Unforeseen'), - (T_seq, π_seq_1, 'Foreseen')], 'ylabel': r'$p$'}, - {'data': [(T_seq, m_seq_2_regime1 - p_seq_2_regime1, 'Unforeseen'), + {'data': [(T_seq, π_seq_2, 'Unforeseen'), + (T_seq, π_seq_1, 'Foreseen')], 'ylabel': r'$\pi$'}, + {'data': [(T_seq, m_seq_2_regime1 - p_seq_2_regime1, 'Unforeseen'), (T_seq, m_seq_1 - p_seq_1, 'Foreseen')], 'ylabel': r'$m - p$'}, - {'data': [(T_seq, m_seq_2_regime1, 'Unforeseen (Smooth $m_{T_1}$)'), + {'data': [(T_seq, m_seq_2_regime1, 'Unforeseen (Smooth $m_{T_1}$)'), (T_seq, m_seq_2_regime2, 'Unforeseen ($m_{T_1}$ jumps)'), - (T_seq, m_seq_1, 'Foreseen')], 'ylabel': r'$m$'}, - {'data': [(T_seq, p_seq_2_regime1, 'Unforeseen (Smooth $m_{T_1}$)'), + (T_seq, m_seq_1, 'Foreseen')], 'ylabel': r'$m$'}, + {'data': [(T_seq, p_seq_2_regime1, 'Unforeseen (Smooth $m_{T_1}$)'), (T_seq, p_seq_2_regime2, 'Unforeseen ($m_{T_1}$ jumps)'), - (T_seq, p_seq_1, 'Foreseen')], 'ylabel': r'$p$'} + (T_seq, p_seq_1, 'Foreseen')], 'ylabel': r'$p$'} ] experiment_plot(plot_configs, ax) @@ -612,7 +613,7 @@ In particular, in the above graphs, notice how a gradual fall in inflation prece inflation instead falls abruptly when the permanent drop in money supply growth is unanticipated. It seems to the author team at quantecon that the drops in inflation near the ends of the four hyperinflations described in {doc}`this lecture ` -more closely resemble outcomes from the experiment 2 "unforeseen stabilization". +more closely resemble outcomes from the experiment 2 "unforeseen stabilization". (It is fair to say that the preceding informal pattern recognition exercise should be supplemented with a more formal structural statistical analysis.) @@ -627,7 +628,7 @@ Thus, suppose that $\phi \in (0,1)$, that $\mu_0 > \mu^*$, and that for $t = 0, $$ \mu_t = \phi^t \mu_0 + (1 - \phi^t) \mu^* . -$$ +$$ Next we perform an experiment in which there is a perfectly foreseen _gradual_ decrease in the rate of growth of the money supply. @@ -643,12 +644,253 @@ cm4 = create_cagan_model(μ_seq=μ_seq_stab) π_seq_4, m_seq_4, p_seq_4 = solve(cm4, T) -sequences = (μ_seq_stab, π_seq_4, +sequences = (μ_seq_stab, π_seq_4, m_seq_4 - p_seq_4, m_seq_4, p_seq_4) -plot_sequences(sequences, (r'$\mu$', r'$\pi$', +plot_sequences(sequences, (r'$\mu$', r'$\pi$', r'$m - p$', r'$m$', r'$p$')) ``` +## Exercises + +```{exercise} +:label: cagan_ex1 + +**Sensitivity to $\alpha$.** + +For Experiment 1 (foreseen sudden stabilization from $\mu_0 = 0.5$ to $\mu^* = 0$ +at $T_1 = 60$, with $T = 80$), solve the model for +$\alpha \in \{1,\, 3,\, 5,\, 10,\, 25\}$ and plot the inflation path $\pi_t$ for +each value on a single graph. + +Describe how the **anticipation effect**, the pre-stabilization fall in inflation, +changes with $\alpha$. +``` + +```{solution-start} cagan_ex1 +:class: dropdown +``` + +```{code-cell} ipython3 +T1 = 60 +μ0 = 0.5 +μ_star = 0.0 +T = 80 +μ_seq = np.append(μ0 * np.ones(T1+1), μ_star * np.ones(T - T1)) + +α_vals = [1, 3, 5, 10, 25] +T_seq = np.arange(T+1) + +fig, ax = plt.subplots() +for α in α_vals: + cm = create_cagan_model(α=α, μ_seq=μ_seq) + π_seq, _, _ = solve(cm, T) + ax.plot(T_seq, π_seq[:-1], label=f'α = {α}') + +ax.axvline(T1, linestyle='--', color='black', lw=1, label='Stabilization $T_1$') +ax.set_xlabel('$t$') +ax.set_ylabel(r'$\pi_t$') +ax.set_title('Inflation paths for different α (foreseen stabilization)') +ax.legend() +plt.show() +``` + +For small $\alpha$, real-balance demand is insensitive to inflation, so +the model behaves almost like the exogenous-money case and inflation tracks $\mu_t$ closely. + +For large $\alpha$, agents strongly revalue money in response to expected +future inflation, so the announcement of a future stabilization pulls +inflation down gradually before $T_1$. + +```{solution-end} +``` + +```{exercise} +:label: cagan_ex2 + +**Verify the analytical formula.** + +For Experiment 1 ($\alpha = 5$, $T_1 = 60$, $T = 80$, $\mu_0 = 0.5$, +$\mu^* = 0$), the closed-form solution for the inflation rate is given by +equation {eq}`eq:fisctheory1`: + +$$ +\pi_t = (1-\delta) \sum_{s=t}^T \delta^{s-t} \mu_s + \delta^{T+1-t} \pi_{T+1}^* +$$ + +Compute $\pi_t$ directly from this formula for each $t = 0, 1, \ldots, T$, +compare it to the matrix solution returned by `solve`, plot both on the same +graph, and print the maximum absolute difference. +``` + +```{solution-start} cagan_ex2 +:class: dropdown +``` + +```{code-cell} ipython3 +T1 = 60 +μ0 = 0.5 +μ_star = 0.0 +T = 80 +α = 5 +μ_seq = np.append(μ0 * np.ones(T1+1), μ_star * np.ones(T - T1)) + +cm = create_cagan_model(α=α, μ_seq=μ_seq) +π_matrix, _, _ = solve(cm, T) +π_matrix = π_matrix[:-1] + +δ = α / (1 + α) +π_term = cm.π_end +π_formula = np.array([ + (1 - δ) * sum(δ**(s-t) * μ_seq[s] for s in range(t, T+1)) + + δ**(T+1-t) * π_term + for t in range(T+1) +]) + +T_seq = np.arange(T+1) +fig, ax = plt.subplots() +ax.plot(T_seq, π_matrix, label='Matrix solution', lw=2) +ax.plot(T_seq, π_formula, '--', label='Analytical formula', lw=1.5) +ax.set_xlabel('$t$') +ax.set_ylabel(r'$\pi_t$') +ax.set_title('Matrix vs analytical inflation path') +ax.legend() +plt.show() + +print(f'Max absolute difference: {np.max(np.abs(π_matrix - π_formula)):.2e}') +``` + +The two methods agree to machine precision, confirming that the matrix system +{eq}`eq:pieq` correctly implements formula {eq}`eq:fisctheory1`. + +```{solution-end} +``` + +```{exercise} +:label: cagan_ex3 + +**Foreseen gradual vs sudden stabilization.** + +Experiment 1 features a *sudden* foreseen drop in money growth at $T_1 = 60$. + +Experiment 3 features a *gradual* foreseen path $\mu_t = \phi^t \mu_0 + (1-\phi^t)\mu^*$. + +On a single graph, plot the inflation paths for: +- Experiment 1 (sudden), and +- Experiment 3 with $\phi \in \{0.95, 0.85, 0.70\}$ (increasingly fast gradualism). + +Use $\alpha = 5$, $\mu_0 = 0.5$, $\mu^* = 0$, and $T = 80$ to determine which +approach generates the smoothest pre-stabilization decline in inflation. +``` + +```{solution-start} cagan_ex3 +:class: dropdown +``` + +```{code-cell} ipython3 +T = 80 +T1 = 60 +μ0 = 0.5 +μ_star = 0.0 +α = 5 +T_seq = np.arange(T+1) + +μ_sudden = np.append(μ0 * np.ones(T1+1), μ_star * np.ones(T - T1)) +cm_sudden = create_cagan_model(α=α, μ_seq=μ_sudden) +π_sudden, _, _ = solve(cm_sudden, T) + +fig, ax = plt.subplots() +ax.plot(T_seq, π_sudden[:-1], lw=2, label='Sudden (Exp. 1)') + +for ϕ in [0.95, 0.85, 0.70]: + μ_grad = np.array([ϕ**t * μ0 + (1 - ϕ**t) * μ_star for t in range(T)]) + μ_grad = np.append(μ_grad, μ_star) + cm_grad = create_cagan_model(α=α, μ_seq=μ_grad) + π_grad, _, _ = solve(cm_grad, T) + ax.plot(T_seq, π_grad[:-1], label=f'Gradual ϕ = {ϕ}') + +ax.set_xlabel('$t$') +ax.set_ylabel(r'$\pi_t$') +ax.set_title('Inflation: sudden vs gradual foreseen stabilization') +ax.legend() +plt.show() +``` + +Faster gradual stabilization, corresponding to a smaller $\phi$, pulls $\mu_t$ down more quickly. + +With less future inflation to discount, $\pi_t$ falls sooner and more steeply. + +The sudden stabilization has the largest discontinuity in the path of $\mu_t$, +but because it is fully anticipated the inflation path is smooth throughout. + +```{solution-end} +``` + +```{exercise} +:label: cagan_ex4 + +**Real-balance dynamics.** + +For Experiments 1 and 2 (foreseen and unforeseen sudden stabilization, regime 1 +where $m_{T_1}$ is kept smooth), compute and plot the path of log real balances +$m_t - p_t$ for $t = 0, 1, \ldots, T$. + +Use $\alpha = 5$, $T_1 = 60$, $T = 80$, $\mu_0 = 0.5$, $\mu^* = 0$. + +Describe the qualitative difference between the two paths and explain it using +the money-demand equation {eq}`eq:caganmd`. +``` + +```{solution-start} cagan_ex4 +:class: dropdown +``` + +```{code-cell} ipython3 +T = 80 +T1 = 60 +μ0 = 0.5 +μ_star = 0.0 +α = 5 + +μ_seq_1 = np.append(μ0 * np.ones(T1+1), μ_star * np.ones(T - T1)) +cm1 = create_cagan_model(α=α, μ_seq=μ_seq_1) +π_seq_1, m_seq_1, p_seq_1 = solve(cm1, T) + +μ_seq_2a = μ0 * np.ones(T+1) +cm2a = create_cagan_model(α=α, μ_seq=μ_seq_2a) +π_pre, m_pre, p_pre = solve(cm2a, T) + +μ_seq_2_cont = μ_star * np.ones(T-T1) +cm2b = create_cagan_model(m0=m_pre[T1+1], α=α, + μ_seq=μ_seq_2_cont) +π_post, m_post, p_post = solve(cm2b, T-1-T1) + +m_unforeseen = np.concatenate((m_pre[:T1+1], m_post)) +p_unforeseen = np.concatenate((p_pre[:T1+1], p_post)) +T_seq = np.arange(T+1) + +fig, ax = plt.subplots() +ax.plot(T_seq, (m_seq_1 - p_seq_1)[:-1], label='Foreseen (Exp. 1)') +ax.plot(T_seq, (m_unforeseen - p_unforeseen)[:-1], + '--', label='Unforeseen (Exp. 2)') +ax.axvline(T1, linestyle=':', color='black', lw=1) +ax.set_xlabel('$t$') +ax.set_ylabel('$m_t - p_t$ (log real balances)') +ax.set_title('Real-balance paths: foreseen vs unforeseen stabilization') +ax.legend() +plt.show() +``` + +From equation {eq}`eq:caganmd`, $m_t - p_t = -\alpha \pi_t$. + +In the foreseen case, inflation falls gradually before $T_1$, so real +balances rise smoothly as the public anticipates lower future inflation. + +In the unforeseen case, there is no pre-announcement effect, so real balances +are flat until the surprise at $T_1$ and then jump upward with the drop in inflation. + +```{solution-end} +``` + ## Sequel Another lecture {doc}`monetarist theory of price levels with adaptive expectations ` describes an "adaptive expectations" version of Cagan's model. diff --git a/lectures/cobweb.md b/lectures/cobweb.md index 3bda73f95..d1c393fca 100644 --- a/lectures/cobweb.md +++ b/lectures/cobweb.md @@ -82,7 +82,6 @@ ax.plot(years, hog_prices, '-o', ms=4, label='hog price') ax.set_xlabel('year') ax.set_ylabel('dollars') ax.legend() -ax.grid() plt.show() ``` @@ -531,7 +530,7 @@ ts_plot_supply(m, 5, 15) Backward looking average expectations refers to the case where producers form expectations for the next period price as a linear combination of their last -guess and the second last guess. +observed price and the price observed one period before that. That is, @@ -588,11 +587,11 @@ def ts_plot_price_blae(model, p0, p1, alphas, ts_length=15): ```{code-cell} ipython3 m = Market() ts_plot_price_blae(m, - p0=5, - p1=6, - alphas=[0.1, 0.3, 0.5, 0.8], + p0=1, + p1=2.5, + alphas=[0.1, 0.3, 0.5, 0.8], ts_length=20) ``` ```{solution-end} -``` \ No newline at end of file +``` diff --git a/lectures/complex_and_trig.md b/lectures/complex_and_trig.md index 7f40497c0..bdeb2ec4e 100644 --- a/lectures/complex_and_trig.md +++ b/lectures/complex_and_trig.md @@ -153,7 +153,6 @@ ax.text(0-0.2, 0.5, '$x = 1$') # Label x ax.text(0.5, 1.2, r'$y = \sqrt{3}$') # Label y ax.text(0.25, 0.15, r'$\theta = 60^o$') # Label θ -ax.grid(True) plt.show() ``` @@ -351,7 +350,6 @@ ticklab = ax.yaxis.get_ticklabels()[0] # Set y-label position trans = ticklab.get_transform() ax.yaxis.set_label_coords(0, 5, transform=trans) -ax.grid() plt.show() ``` @@ -526,3 +524,207 @@ integrate(sin(ω)**2, (ω, -pi, pi)) ```{solution-end} ``` + +```{exercise} +:label: complex_ex2 + +**Double-angle identities via De Moivre's theorem.** + +Apply de Moivre's theorem with $n = 2$: + +$$ +(\cos\theta + i\sin\theta)^2 = \cos 2\theta + i\sin 2\theta +$$ + +Expand the left-hand side as a complex square and equate real and imaginary +parts to deduce the two **double-angle identities** + +$$ +\cos 2\theta = \cos^2\theta - \sin^2\theta, \qquad +\sin 2\theta = 2\sin\theta\cos\theta. +$$ + +Then use the Pythagorean identity $\cos^2\theta + \sin^2\theta = 1$ to write +two alternative forms of the cosine identity: + +$$ +\cos 2\theta = 2\cos^2\theta - 1 = 1 - 2\sin^2\theta. +$$ + +Verify all four identities using `simplify` from `sympy`. +``` + +```{solution-start} complex_ex2 +:class: dropdown +``` + +De Moivre with $n = 2$ gives +$(\cos\theta + i\sin\theta)^2 = \cos 2\theta + i\sin 2\theta$. +Expanding the left side: + +$$ +\cos^2\theta - \sin^2\theta \;+\; i\,(2\sin\theta\cos\theta) += \cos 2\theta + i\sin 2\theta. +$$ + +Matching real parts gives $\cos 2\theta = \cos^2\theta - \sin^2\theta$. + +Matching imaginary parts gives $\sin 2\theta = 2\sin\theta\cos\theta$. + +Substituting $\sin^2\theta = 1 - \cos^2\theta$ into the cosine formula gives +$\cos 2\theta = 2\cos^2\theta - 1$, and substituting $\cos^2\theta = 1 - \sin^2\theta$ +gives $\cos 2\theta = 1 - 2\sin^2\theta$. + +```{code-cell} ipython3 +from sympy import Symbol, cos, sin, simplify + +θ = Symbol('θ', real=True) + +print("cos(2θ) = cos(θ)**2 - sin(θ)**2:", + simplify(cos(2*θ) - (cos(θ)**2 - sin(θ)**2))) + +print("sin(2θ) = 2sinθcosθ:", + simplify(sin(2*θ) - 2*sin(θ)*cos(θ))) + +print("cos(2θ) = 2*cos(θ)**2 - 1:", + simplify(cos(2*θ) - (2*cos(θ)**2 - 1))) + +print("cos(2θ) = 1 - 2*sin(θ)**2:", + simplify(cos(2*θ) - (1 - 2*sin(θ)**2))) +``` + +Each `simplify` call returns 0, confirming all four identities. + +```{solution-end} +``` + +```{exercise} +:label: complex_ex3 + +**Product-to-sum formulas by "adding appropriate pairs".** + +The angle-sum identities derived in the lecture are: + +$$ +\cos(\theta + w) = \cos\theta\cos w - \sin\theta\sin w +$$ (ct-cos-sum) + +$$ +\cos(\theta - w) = \cos\theta\cos w + \sin\theta\sin w +$$ (ct-cos-diff) + +$$ +\sin(\theta + w) = \sin\theta\cos w + \cos\theta\sin w +$$ (ct-sin-sum) + +$$ +\sin(\theta - w) = \sin\theta\cos w - \cos\theta\sin w +$$ (ct-sin-diff) + +By adding and subtracting appropriate pairs of equations {eq}`ct-cos-sum`--{eq}`ct-sin-diff`, derive the three **product-to-sum formulas**: + +$$ +\cos\theta\cos w = \frac{\cos(\theta+w) + \cos(\theta-w)}{2} +$$ + +$$ +\sin\theta\sin w = \frac{\cos(\theta-w) - \cos(\theta+w)}{2} +$$ + +$$ +\sin\theta\cos w = \frac{\sin(\theta+w) + \sin(\theta-w)}{2} +$$ + +Verify all three with `simplify` from `sympy`. +``` + +```{solution-start} complex_ex3 +:class: dropdown +``` + +Adding (i) and (ii) gives $\cos(\theta+w) + \cos(\theta-w) = 2\cos\theta\cos w$. + +Subtracting (i) from (ii) gives $\cos(\theta-w) - \cos(\theta+w) = 2\sin\theta\sin w$. + +Adding (iii) and (iv) gives $\sin(\theta+w) + \sin(\theta-w) = 2\sin\theta\cos w$. + +Dividing each result by 2 yields the three product-to-sum formulas. + +```{code-cell} ipython3 +from sympy import symbols, cos, sin, simplify + +θ, w = symbols('θ w', real=True) + +print("cos(θ+w) + cos(θ-w) - 2cos(θ)cos(w) =", + simplify(cos(θ+w) + cos(θ-w) - 2*cos(θ)*cos(w))) + +print("cos(θ-w) - cos(θ+w) - 2sin(θ)sin(w) =", + simplify(cos(θ-w) - cos(θ+w) - 2*sin(θ)*sin(w))) + +print("sin(θ+w) + sin(θ-w) - 2sin(θ)cos(w) =", + simplify(sin(θ+w) + sin(θ-w) - 2*sin(θ)*cos(w))) +``` + +All three expressions simplify to 0. + +```{solution-end} +``` + +```{exercise} +:label: complex_ex4 + +**Orthogonality of cosines.** + +Apply the product-to-sum formula from {ref}`complex_ex3` with $\theta = m\phi$ +and $w = n\phi$: + +$$ +\cos(m\phi)\cos(n\phi) = \frac{\cos((m-n)\phi) + \cos((m+n)\phi)}{2}. +$$ + +Use this identity and the fact that $\int_{-\pi}^{\pi} \cos(k\phi)\,d\phi = 0$ +for every non-zero integer $k$ to prove that for positive integers $m$ and $n$ + +$$ +\int_{-\pi}^{\pi} \cos(m\phi)\cos(n\phi)\,d\phi = +\begin{cases} \pi & \text{if } m = n \\ 0 & \text{if } m \neq n. \end{cases} +$$ + +Verify this **orthogonality table** numerically with `integrate` from `sympy` +for $m, n \in \{1, 2, 3\}$. +``` + +```{solution-start} complex_ex4 +:class: dropdown +``` + +**Case $m \neq n$:** Both $m - n$ and $m + n$ are non-zero integers, so +$\int_{-\pi}^{\pi} \cos((m-n)\phi)\,d\phi = \int_{-\pi}^{\pi} \cos((m+n)\phi)\,d\phi = 0$, +giving a total of 0. + +**Case $m = n$:** The formula becomes +$\cos(m\phi)^2 = \tfrac{1}{2}[1 + \cos(2m\phi)]$. + +Since $\int_{-\pi}^{\pi} \cos(2m\phi)\,d\phi = 0$ for non-zero integer $m$, +the integral equals $\tfrac{1}{2} \cdot 2\pi = \pi$. + +```{code-cell} ipython3 +from sympy import Symbol, cos, integrate, pi + +ϕ = Symbol('ϕ', real=True) + +print(f"{'m':>3} {'n':>3} {'integral':>10}") +print('-' * 22) +for m in [1, 2, 3]: + for n in [1, 2, 3]: + val = integrate(cos(m*ϕ) * cos(n*ϕ), (ϕ, -pi, pi)) + print(f"{m:>3} {n:>3} {str(val):>10}") +``` + +The table confirms the rule that diagonal entries, where $m = n$, all equal $\pi$ and every off-diagonal entry equals $0$. + +This orthogonality property is the foundation of Fourier series because sinusoidal +functions at different frequencies do not "interfere" when we decompose a signal into its frequency components. + +```{solution-end} +``` diff --git a/lectures/cons_smooth.md b/lectures/cons_smooth.md index 626f59565..4f74ed772 100644 --- a/lectures/cons_smooth.md +++ b/lectures/cons_smooth.md @@ -18,20 +18,20 @@ kernelspec: In this lecture, we'll study a famous model of the "consumption function" that Milton Friedman {cite}`Friedman1956` and Robert Hall {cite}`Hall1978`) proposed to fit some empirical data patterns that the original Keynesian consumption function described in this QuantEcon lecture {doc}`geometric series ` missed. -We'll study what is often called the "consumption-smoothing model." +We'll study what is often called the "consumption-smoothing model." -We'll use matrix multiplication and matrix inversion, the same tools that we used in this QuantEcon lecture {doc}`present values `. +We'll use matrix multiplication and matrix inversion, the same tools that we used in this QuantEcon lecture {doc}`present values `. Formulas presented in {doc}`present value formulas` are at the core of the consumption-smoothing model because we shall use them to define a consumer's "human wealth". The key idea that inspired Milton Friedman was that a person's non-financial income, i.e., his or her wages from working, can be viewed as a dividend stream from ''human capital'' -and that standard asset-pricing formulas can be applied to compute -''non-financial wealth'' that capitalizes that earnings stream. +and that standard asset-pricing formulas can be applied to compute +''non-financial wealth'' that capitalizes that earnings stream. ```{note} As we'll see in this QuantEcon lecture {doc}`equalizing difference model `, -Milton Friedman had used this idea in his PhD thesis at Columbia University, +Milton Friedman had used this idea in his PhD thesis at Columbia University, eventually published as {cite}`kuznets1939incomes` and {cite}`friedman1954incomes`. ``` @@ -50,28 +50,28 @@ from collections import namedtuple The model describes a consumer who lives from time $t=0, 1, \ldots, T$, receives a stream $\{y_t\}_{t=0}^T$ of non-financial income and chooses a consumption stream $\{c_t\}_{t=0}^T$. -We usually think of the non-financial income stream as coming from the person's earnings from supplying labor. +We usually think of the non-financial income stream as coming from the person's earnings from supplying labor. -The model takes a non-financial income stream as an input, regarding it as "exogenous" in the sense that it is determined outside the model. +The model takes a non-financial income stream as an input, regarding it as "exogenous" in the sense that it is determined outside the model. The consumer faces a gross interest rate of $R >1$ that is constant over time, at which she is free to borrow or lend, up to limits that we'll describe below. -Let +Let - * $T \geq 2$ be a positive integer that constitutes a time-horizon. - * $y = \{y_t\}_{t=0}^T$ be an exogenous sequence of non-negative non-financial incomes $y_t$. - * $a = \{a_t\}_{t=0}^{T+1}$ be a sequence of financial wealth. - * $c = \{c_t\}_{t=0}^T$ be a sequence of non-negative consumption rates. - * $R \geq 1$ be a fixed gross one period rate of return on financial assets. - * $\beta \in (0,1)$ be a fixed discount factor. + * $T \geq 2$ be a positive integer that constitutes a time-horizon. + * $y = \{y_t\}_{t=0}^T$ be an exogenous sequence of non-negative non-financial incomes $y_t$. + * $a = \{a_t\}_{t=0}^{T+1}$ be a sequence of financial wealth. + * $c = \{c_t\}_{t=0}^T$ be a sequence of non-negative consumption rates. + * $R \geq 1$ be a fixed gross one period rate of return on financial assets. + * $\beta \in (0,1)$ be a fixed discount factor. * $a_0$ be a given initial level of financial assets - * $a_{T+1} \geq 0$ be a terminal condition on final assets. + * $a_{T+1} \geq 0$ be a terminal condition on final assets. The sequence of financial wealth $a$ is to be determined by the model. We require it to satisfy two **boundary conditions**: - * it must equal an exogenous value $a_0$ at time $0$ + * it must equal an exogenous value $a_0$ at time $0$ * it must equal or exceed an exogenous value $a_{T+1}$ at time $T+1$. The **terminal condition** $a_{T+1} \geq 0$ requires that the consumer not leave the model in debt. @@ -85,23 +85,23 @@ $$ a_{t+1} = R (a_t+ y_t - c_t), \quad t =0, 1, \ldots T $$ (eq:a_t) -Equations {eq}`eq:a_t` constitute $T+1$ such budget constraints, one for each $t=0, 1, \ldots, T$. +Equations {eq}`eq:a_t` constitute $T+1$ such budget constraints, one for each $t=0, 1, \ldots, T$. -Given a sequence $y$ of non-financial incomes, a large set of pairs $(a, c)$ of (financial wealth, consumption) sequences satisfy the sequence of budget constraints {eq}`eq:a_t`. +Given a sequence $y$ of non-financial incomes, a large set of pairs $(a, c)$ of (financial wealth, consumption) sequences satisfy the sequence of budget constraints {eq}`eq:a_t`. Our model has the following logical flow. - * start with an exogenous non-financial income sequence $y$, an initial financial wealth $a_0$, and + * start with an exogenous non-financial income sequence $y$, an initial financial wealth $a_0$, and a candidate consumption path $c$. - + * use the system of equations {eq}`eq:a_t` for $t=0, \ldots, T$ to compute a path $a$ of financial wealth - - * verify that $a_{T+1}$ satisfies the terminal wealth constraint $a_{T+1} \geq 0$. - - * If it does, declare that the candidate path is **budget feasible**. - + + * verify that $a_{T+1}$ satisfies the terminal wealth constraint $a_{T+1} \geq 0$. + + * If it does, declare that the candidate path is **budget feasible**. + * if the candidate consumption path is not budget feasible, propose a less greedy consumption path and start over - + Below, we'll describe how to execute these steps using linear algebra -- matrix inversion and multiplication. The above procedure seems like a sensible way to find "budget-feasible" consumption paths $c$, i.e., paths that are consistent @@ -120,33 +120,33 @@ To answer this question, we shall eventually evaluate alternative budget feasibl W = \sum_{t=0}^T \beta^t (g_1 c_t - \frac{g_2}{2} c_t^2 ) ``` -where $g_1 > 0, g_2 > 0$. +where $g_1 > 0, g_2 > 0$. -When $\beta R \approx 1$, the fact that the utility function $g_1 c_t - \frac{g_2}{2} c_t^2$ has diminishing marginal utility imparts a preference for consumption that is very smooth. +When $\beta R \approx 1$, the fact that the utility function $g_1 c_t - \frac{g_2}{2} c_t^2$ has diminishing marginal utility imparts a preference for consumption that is very smooth. Indeed, we shall see that when $\beta R = 1$ (a condition assumed by Milton Friedman {cite}`Friedman1956` and Robert Hall {cite}`Hall1978`), criterion {eq}`welfare` assigns higher welfare to smoother consumption paths. -By **smoother** we mean as close as possible to being constant over time. +By **smoother** we mean as close as possible to being constant over time. The preference for smooth consumption paths that is built into the model gives it the name "consumption-smoothing model". We'll postpone verifying our claim that a constant consumption path is optimal when $\beta R=1$ -by comparing welfare levels that comes from a constant path with ones that involve non-constant paths. +by comparing welfare levels that comes from a constant path with ones that involve non-constant paths. Before doing that, let's dive in and do some calculations that will help us understand how the model works in practice when we provide the consumer with some different streams on non-financial income. -Here we use default parameters $R = 1.05$, $g_1 = 1$, $g_2 = 1/2$, and $T = 65$. +Here we use default parameters $R = 1.05$, $g_1 = 1$, $g_2 = 1/2$, and $T = 65$. We create a Python **namedtuple** to store these parameters with default values. ```{code-cell} ipython3 -ConsumptionSmoothing = namedtuple("ConsumptionSmoothing", +ConsumptionSmoothing = namedtuple("ConsumptionSmoothing", ["R", "g1", "g2", "β_seq", "T"]) def create_consumption_smoothing_model(R=1.05, g1=1, g2=1/2, T=65): β = 1/R β_seq = np.array([β**i for i in range(T+1)]) - return ConsumptionSmoothing(R, g1, g2, + return ConsumptionSmoothing(R, g1, g2, β_seq, T) ``` @@ -160,13 +160,13 @@ h_0 \equiv \sum_{t=0}^T R^{-t} y_t = \begin{bmatrix} 1 & R^{-1} & \cdots & R^{-T \begin{bmatrix} y_0 \cr y_1 \cr \vdots \cr y_T \end{bmatrix} $$ -Human or non-financial wealth at time $0$ is evidently just the present value of the consumer's non-financial income stream $y$. +Human or non-financial wealth at time $0$ is evidently just the present value of the consumer's non-financial income stream $y$. Formally it very much resembles the asset price that we computed in this QuantEcon lecture {doc}`present values `. -Indeed, this is why Milton Friedman called it "human capital". +Indeed, this is why Milton Friedman called it "human capital". -By iterating on equation {eq}`eq:a_t` and imposing the terminal condition +By iterating on equation {eq}`eq:a_t` and imposing the terminal condition $$ a_{T+1} = 0, @@ -174,22 +174,22 @@ $$ it is possible to convert a sequence of budget constraints {eq}`eq:a_t` into a single intertemporal constraint -$$ -\sum_{t=0}^T R^{-t} c_t = a_0 + h_0. +$$ +\sum_{t=0}^T R^{-t} c_t = a_0 + h_0. $$ (eq:budget_intertemp) Equation {eq}`eq:budget_intertemp` says that the present value of the consumption stream equals the sum of financial and non-financial (or human) wealth. -Robert Hall {cite}`Hall1978` showed that when $\beta R = 1$, a condition Milton Friedman had also assumed, it is "optimal" for a consumer to smooth consumption by setting +Robert Hall {cite}`Hall1978` showed that when $\beta R = 1$, a condition Milton Friedman had also assumed, it is "optimal" for a consumer to smooth consumption by setting -$$ +$$ c_t = c_0 \quad t =0, 1, \ldots, T $$ (Later we'll present a "variational argument" that shows that this constant path maximizes criterion {eq}`welfare` when $\beta R =1$.) -In this case, we can use the intertemporal budget constraint to write +In this case, we can use the intertemporal budget constraint to write $$ c_t = c_0 = \left(\sum_{t=0}^T R^{-t}\right)^{-1} (a_0 + h_0), \quad t= 0, 1, \ldots, T. @@ -198,7 +198,7 @@ $$ (eq:conssmoothing) Equation {eq}`eq:conssmoothing` is the consumption-smoothing model in a nutshell. -## Mechanics of consumption-smoothing model +## Mechanics of consumption-smoothing model As promised, we'll provide step-by-step instructions on how to use linear algebra, readily implemented in Python, to compute all objects in play in the consumption-smoothing model. @@ -229,17 +229,17 @@ To do this, we translate that system of difference equations into a single matri $$ -\begin{bmatrix} +\begin{bmatrix} 1 & 0 & 0 & \cdots & 0 & 0 & 0 \cr -R & 1 & 0 & \cdots & 0 & 0 & 0 \cr 0 & -R & 1 & \cdots & 0 & 0 & 0 \cr \vdots &\vdots & \vdots & \cdots & \vdots & \vdots & \vdots \cr 0 & 0 & 0 & \cdots & -R & 1 & 0 \cr 0 & 0 & 0 & \cdots & 0 & -R & 1 -\end{bmatrix} -\begin{bmatrix} a_1 \cr a_2 \cr a_3 \cr \vdots \cr a_T \cr a_{T+1} \end{bmatrix} -= R +\begin{bmatrix} a_1 \cr a_2 \cr a_3 \cr \vdots \cr a_T \cr a_{T+1} +\end{bmatrix} += R \begin{bmatrix} y_0 + a_0 - c_0 \cr y_1 - c_0 \cr y_2 - c_0 \cr \vdots\cr y_{T-1} - c_0 \cr y_T - c_0 \end{bmatrix} $$ @@ -252,12 +252,12 @@ $$ Because we have built into our calculations that the consumer leaves the model with exactly zero assets, just barely satisfying the -terminal condition that $a_{T+1} \geq 0$, it should turn out that +terminal condition that $a_{T+1} \geq 0$, it should turn out that $$ a_{T+1} = 0. $$ - + Let's verify this with Python code. @@ -303,7 +303,7 @@ y_seq = np.concatenate([np.ones(46), np.zeros(20)]) cs_model = create_consumption_smoothing_model() c_seq, a_seq, h0 = compute_optimal(cs_model, a0, y_seq) -print('check a_T+1=0:', +print('check a_T+1=0:', np.abs(a_seq[-1] - 0) <= 1e-8) ``` @@ -352,30 +352,30 @@ First we create a function `plot_cs` that generates graphs for different instan This will help us avoid rewriting code to plot outcomes for different non-financial income sequences. ```{code-cell} ipython3 -def plot_cs(model, # consumption-smoothing model +def plot_cs(model, # consumption-smoothing model a0, # initial financial wealth y_seq # non-financial income process ): - + # Compute optimal consumption c_seq, a_seq, h0 = compute_optimal(model, a0, y_seq) - + # Sequence length T = cs_model.T - + fig, axes = plt.subplots(1, 2, figsize=(12,5)) - + axes[0].plot(range(T+1), y_seq, label='non-financial income', lw=2) axes[0].plot(range(T+1), c_seq, label='consumption', lw=2) axes[1].plot(range(T+2), a_seq, label='financial wealth', color='green', lw=2) axes[0].set_ylabel(r'$c_t,y_t$') axes[1].set_ylabel(r'$a_t$') - + for ax in axes: ax.plot(range(T+2), np.zeros(T+2), '--', lw=1, color='black') ax.legend() ax.set_xlabel(r'$t$') - + plt.show() ``` @@ -383,7 +383,7 @@ In the experiments below, please study how consumption and financial asset seque #### Experiment 1: one-time gain/loss -We first assume a one-time windfall of $W_0$ in year 21 of the income sequence $y$. +We first assume a one-time windfall of $W_0$ in year 21 of the income sequence $y$. We'll make $W_0$ big - positive to indicate a one-time windfall, and negative to indicate a one-time "disaster". @@ -448,7 +448,7 @@ y_0 = 1 t_max = 46 # Generate geometric y sequence -geo_seq = λ ** np.arange(t_max) * y_0 +geo_seq = λ ** np.arange(t_max) * y_0 y_seq_geo = np.concatenate( [geo_seq, np.zeros(20)]) @@ -460,7 +460,7 @@ Now we show the behavior when $\lambda = 0.95$ ```{code-cell} ipython3 λ = 0.95 -geo_seq = λ ** np.arange(t_max) * y_0 +geo_seq = λ ** np.arange(t_max) * y_0 y_seq_geo = np.concatenate( [geo_seq, np.zeros(20)]) @@ -481,13 +481,13 @@ plot_cs(cs_model, a0, y_seq_geo) ### Feasible consumption variations -We promised to justify our claim that when $\beta R =1$ as Friedman assumed, a constant consumption play $c_t = c_0$ for all $t$ is optimal. +We promised to justify our claim that when $\beta R =1$ as Friedman assumed, a constant consumption play $c_t = c_0$ for all $t$ is optimal. Let's do that now. -The approach we'll take is an elementary example of the "calculus of variations". +The approach we'll take is an elementary example of the "calculus of variations". -Let's dive in and see what the key idea is. +Let's dive in and see what the key idea is. To explore what types of consumption paths are welfare-improving, we shall create an **admissible consumption path variation sequence** $\{v_t\}_{t=0}^T$ that satisfies @@ -509,7 +509,7 @@ $$ v_t = \xi_1 \phi^t - \xi_0 $$ -We say two and not three-parameter class because $\xi_0$ will be a function of $(\phi, \xi_1; R)$ that guarantees that the variation sequence is feasible. +We say two and not three-parameter class because $\xi_0$ will be a function of $(\phi, \xi_1; R)$ that guarantees that the variation sequence is feasible. Let's compute that function. @@ -522,7 +522,7 @@ $$ which implies that $$ -\xi_1 \sum_{t=0}^T \phi_t R^{-t} - \xi_0 \sum_{t=0}^T R^{-t} = 0 +\xi_1 \sum_{t=0}^T \phi^t R^{-t} - \xi_0 \sum_{t=0}^T R^{-t} = 0 $$ which implies that @@ -535,9 +535,9 @@ which implies that $$ \xi_0 = \xi_0(\phi, \xi_1; R) = \xi_1 \left(\frac{1 - R^{-1}}{1 - R^{-(T+1)}}\right) \left(\frac{1 - (\phi R^{-1})^{T+1}}{1 - \phi R^{-1}}\right) -$$ +$$ -This is our formula for $\xi_0$. +This is our formula for $\xi_0$. **Key Idea:** if $c^o$ is a budget-feasible consumption path, then so is $c^o + v$, where $v$ is a budget-feasible variation. @@ -551,9 +551,16 @@ Now let's compute and plot consumption path variations def compute_variation(model, ξ1, ϕ, a0, y_seq, verbose=1): R, T, β_seq = model.R, model.T, model.β_seq - ξ0 = ξ1*((1 - 1/R) / (1 - (1/R)**(T+1))) * ((1 - (ϕ/R)**(T+1)) / (1 - ϕ/R)) + growth = ϕ / R + if np.isclose(growth, 1): + pv_sum = T + 1 + else: + pv_sum = (1 - growth**(T+1)) / (1 - growth) + + annuity = (1 - 1/R) / (1 - (1/R)**(T+1)) + ξ0 = ξ1 * annuity * pv_sum v_seq = np.array([(ξ1*ϕ**t - ξ0) for t in range(T+1)]) - + if verbose == 1: print('check feasible:', np.isclose(β_seq @ v_seq, 0)) # since β = 1/R @@ -577,20 +584,20 @@ params = np.array(np.meshgrid(ξ1s, ϕs)).T.reshape(-1, 2) for i, param in enumerate(params): ξ1, ϕ = param print(f'variation {i}: ξ1={ξ1}, ϕ={ϕ}') - cvar_seq = compute_variation(model=cs_model, - ξ1=ξ1, ϕ=ϕ, a0=a0, + cvar_seq = compute_variation(model=cs_model, + ξ1=ξ1, ϕ=ϕ, a0=a0, y_seq=y_seq) print(f'welfare={welfare(cs_model, cvar_seq)}') print('-'*64) if i % 2 == 0: ls = '-.' - else: - ls = '-' - ax.plot(range(T+1), cvar_seq, ls=ls, - color=colors[ξ1], + else: + ls = '-' + ax.plot(range(T+1), cvar_seq, ls=ls, + color=colors[ξ1], label=fr'$\xi_1 = {ξ1}, \phi = {ϕ}$') -plt.plot(range(T+1), c_seq, +plt.plot(range(T+1), c_seq, color='orange', label=r'Optimal $\vec{c}$ ') plt.legend() @@ -599,7 +606,7 @@ plt.ylabel(r'$c_t$') plt.show() ``` -We can even use the Python `np.gradient` command to compute derivatives of welfare with respect to our two parameters. +We can even use the Python `np.gradient` command to compute derivatives of welfare with respect to our two parameters. (We are actually discovering the key idea beneath the **calculus of variations**.) @@ -608,13 +615,13 @@ First, we define the welfare with respect to $\xi_1$ and $\phi$ ```{code-cell} ipython3 def welfare_rel(ξ1, ϕ): """ - Compute welfare of variation sequence + Compute welfare of variation sequence for given ϕ, ξ1 with a consumption-smoothing model """ - - cvar_seq = compute_variation(cs_model, ξ1=ξ1, - ϕ=ϕ, a0=a0, - y_seq=y_seq, + + cvar_seq = compute_variation(cs_model, ξ1=ξ1, + ϕ=ϕ, a0=a0, + y_seq=y_seq, verbose=0) return welfare(cs_model, cvar_seq) @@ -661,22 +668,22 @@ plt.show() ## Wrapping up the consumption-smoothing model The consumption-smoothing model of Milton Friedman {cite}`Friedman1956` and Robert Hall {cite}`Hall1978`) is a cornerstone of modern economics that has important ramifications for the size of the Keynesian "fiscal policy multiplier" that we described in -QuantEcon lecture {doc}`geometric series `. +QuantEcon lecture {doc}`geometric series `. The consumption-smoothingmodel **lowers** the government expenditure multiplier relative to one implied by the original Keynesian consumption function presented in {doc}`geometric series `. -Friedman's work opened the door to an enlightening literature on the aggregate consumption function and associated government expenditure multipliers that remains active today. +Friedman's work opened the door to an enlightening literature on the aggregate consumption function and associated government expenditure multipliers that remains active today. ## Appendix: solving difference equations with linear algebra -In the preceding sections we have used linear algebra to solve a consumption-smoothing model. +In the preceding sections we have used linear algebra to solve a consumption-smoothing model. The same tools from linear algebra -- matrix multiplication and matrix inversion -- can be used to study many other dynamic models. We'll conclude this lecture by giving a couple of examples. -We'll describe a useful way of representing and "solving" linear difference equations. +We'll describe a useful way of representing and "solving" linear difference equations. To generate some $y$ vectors, we'll just write down a linear difference equation with appropriate initial conditions and then use linear algebra to solve it. @@ -695,19 +702,19 @@ where $y_0$ is a given initial condition. We can cast this set of $T$ equations as a single matrix equation $$ -\begin{bmatrix} +\begin{bmatrix} 1 & 0 & 0 & \cdots & 0 & 0 \cr -\lambda & 1 & 0 & \cdots & 0 & 0 \cr 0 & -\lambda & 1 & \cdots & 0 & 0 \cr \vdots & \vdots & \vdots & \cdots & \vdots & \vdots \cr -0 & 0 & 0 & \cdots & -\lambda & 1 -\end{bmatrix} +0 & 0 & 0 & \cdots & -\lambda & 1 +\end{bmatrix} \begin{bmatrix} -y_1 \cr y_2 \cr y_3 \cr \vdots \cr y_T +y_1 \cr y_2 \cr y_3 \cr \vdots \cr y_T \end{bmatrix} -= -\begin{bmatrix} -\lambda y_0 \cr 0 \cr 0 \cr \vdots \cr 0 += +\begin{bmatrix} +\lambda y_0 \cr 0 \cr 0 \cr \vdots \cr 0 \end{bmatrix} $$ (eq:first_order_lin_diff) @@ -717,34 +724,34 @@ Multiplying both sides of {eq}`eq:first_order_lin_diff` by the inverse of the ```{math} :label: fst_ord_inverse -\begin{bmatrix} -y_1 \cr y_2 \cr y_3 \cr \vdots \cr y_T -\end{bmatrix} -= -\begin{bmatrix} +\begin{bmatrix} +y_1 \cr y_2 \cr y_3 \cr \vdots \cr y_T +\end{bmatrix} += +\begin{bmatrix} 1 & 0 & 0 & \cdots & 0 & 0 \cr \lambda & 1 & 0 & \cdots & 0 & 0 \cr \lambda^2 & \lambda & 1 & \cdots & 0 & 0 \cr \vdots & \vdots & \vdots & \cdots & \vdots & \vdots \cr -\lambda^{T-1} & \lambda^{T-2} & \lambda^{T-3} & \cdots & \lambda & 1 +\lambda^{T-1} & \lambda^{T-2} & \lambda^{T-3} & \cdots & \lambda & 1 \end{bmatrix} -\begin{bmatrix} -\lambda y_0 \cr 0 \cr 0 \cr \vdots \cr 0 +\begin{bmatrix} +\lambda y_0 \cr 0 \cr 0 \cr \vdots \cr 0 \end{bmatrix} ``` ```{exercise} :label: consmooth_ex1 -To get {eq}`fst_ord_inverse`, we multiplied both sides of {eq}`eq:first_order_lin_diff` by the inverse of the matrix $A$. Please confirm that +To get {eq}`fst_ord_inverse`, we multiplied both sides of {eq}`eq:first_order_lin_diff` by the inverse of the matrix $A$. Please confirm that $$ -\begin{bmatrix} +\begin{bmatrix} 1 & 0 & 0 & \cdots & 0 & 0 \cr \lambda & 1 & 0 & \cdots & 0 & 0 \cr \lambda^2 & \lambda & 1 & \cdots & 0 & 0 \cr \vdots & \vdots & \vdots & \cdots & \vdots & \vdots \cr -\lambda^{T-1} & \lambda^{T-2} & \lambda^{T-3} & \cdots & \lambda & 1 +\lambda^{T-1} & \lambda^{T-2} & \lambda^{T-3} & \cdots & \lambda & 1 \end{bmatrix} $$ @@ -752,6 +759,32 @@ is the inverse of $A$ and check that $A A^{-1} = I$ ``` +```{solution-start} consmooth_ex1 +:class: dropdown +``` + +```{code-cell} ipython3 +λ = 0.9 +T = 6 + +# Matrix A: ones on diagonal, -λ on the first subdiagonal +A = np.eye(T) - λ * np.diag(np.ones(T-1), k=-1) + +# Purported inverse: lower triangular, A_inv[i,j] = λ^(i-j) for i >= j +A_inv = np.zeros((T, T)) +for i in range(T): + for j in range(i + 1): + A_inv[i, j] = λ**(i - j) + +# Verify A @ A_inv = I +print("A @ A_inv (should be identity):") +print(np.round(A @ A_inv, 10)) +print("Is identity:", np.allclose(A @ A_inv, np.eye(T))) +``` + +```{solution-end} +``` + ### Second-order difference equation A second-order linear difference equation for $\{y_t\}_{t=0}^T$ is @@ -760,24 +793,24 @@ $$ y_{t} = \lambda_1 y_{t-1} + \lambda_2 y_{t-2}, \quad t = 1, 2, \ldots, T $$ -where now $y_0$ and $y_{-1}$ are two given initial equations determined outside the model. +where now $y_0$ and $y_{-1}$ are two given initial equations determined outside the model. As we did with the first-order difference equation, we can cast this set of $T$ equations as a single matrix equation $$ -\begin{bmatrix} +\begin{bmatrix} 1 & 0 & 0 & \cdots & 0 & 0 & 0 \cr -\lambda_1 & 1 & 0 & \cdots & 0 & 0 & 0 \cr -\lambda_2 & -\lambda_1 & 1 & \cdots & 0 & 0 & 0 \cr \vdots & \vdots & \vdots & \cdots & \vdots & \vdots \cr -0 & 0 & 0 & \cdots & -\lambda_2 & -\lambda_1 & 1 -\end{bmatrix} -\begin{bmatrix} -y_1 \cr y_2 \cr y_3 \cr \vdots \cr y_T +0 & 0 & 0 & \cdots & -\lambda_2 & -\lambda_1 & 1 \end{bmatrix} -= -\begin{bmatrix} -\lambda_1 y_0 + \lambda_2 y_{-1} \cr \lambda_2 y_0 \cr 0 \cr \vdots \cr 0 +\begin{bmatrix} +y_1 \cr y_2 \cr y_3 \cr \vdots \cr y_T +\end{bmatrix} += +\begin{bmatrix} +\lambda_1 y_0 + \lambda_2 y_{-1} \cr \lambda_2 y_0 \cr 0 \cr \vdots \cr 0 \end{bmatrix} $$ @@ -789,3 +822,215 @@ Multiplying both sides by inverse of the matrix on the left again provides the As an exercise, we ask you to represent and solve a **third-order linear difference equation**. How many initial conditions must you specify? ``` + +```{solution-start} consmooth_ex2 +:class: dropdown +``` + +A third-order linear difference equation is + +$$ +y_t = \lambda_1 y_{t-1} + \lambda_2 y_{t-2} + \lambda_3 y_{t-3}, \quad t = 1, 2, \ldots, T +$$ + +Three initial conditions are required: $y_0$, $y_{-1}$, and $y_{-2}$. + +The matrix representation stacks the $T$ equations as + +$$ +\begin{bmatrix} +1 & 0 & 0 & 0 & \cdots \\ +-\lambda_1 & 1 & 0 & 0 & \cdots \\ +-\lambda_2 & -\lambda_1 & 1 & 0 & \cdots \\ +-\lambda_3 & -\lambda_2 & -\lambda_1 & 1 & \cdots \\ +\vdots & \ddots & \ddots & \ddots & \ddots +\end{bmatrix} +\begin{bmatrix} y_1 \\ y_2 \\ y_3 \\ y_4 \\ \vdots \end{bmatrix} += +\begin{bmatrix} +\lambda_1 y_0 + \lambda_2 y_{-1} + \lambda_3 y_{-2} \\ +\lambda_2 y_0 + \lambda_3 y_{-1} \\ +\lambda_3 y_0 \\ +0 \\ +\vdots +\end{bmatrix} +$$ + +```{code-cell} ipython3 +λ1, λ2, λ3 = 0.8, -0.3, 0.1 +y0, y_m1, y_m2 = 1.0, 0.5, 0.25 +T = 15 + +# Build the 3rd-order coefficient matrix +A3 = np.eye(T) +for i in range(T): + if i >= 1: + A3[i, i-1] = -λ1 + if i >= 2: + A3[i, i-2] = -λ2 + if i >= 3: + A3[i, i-3] = -λ3 + +# Right-hand side +b = np.zeros(T) +b[0] = λ1 * y0 + λ2 * y_m1 + λ3 * y_m2 +b[1] = λ2 * y0 + λ3 * y_m1 +b[2] = λ3 * y0 + +# Solve +y = np.linalg.solve(A3, b) +y_full = np.concatenate([[y_m2, y_m1, y0], y]) + +fig, ax = plt.subplots() +ax.plot(range(-2, T+1), y_full, 'o-') +ax.axhline(0, linestyle='--', lw=1) +ax.set_xlabel('$t$') +ax.set_ylabel('$y_t$') +ax.set_title('Third-order linear difference equation') +plt.show() +``` + +```{solution-end} +``` + +## Exercises + +```{exercise} +:label: consmooth_ex3 + +Using `compute_optimal`, compute the optimal constant consumption level $c_0$ for +initial financial wealth values $a_0 \in \{-4, -2, 0, 2\}$, holding fixed the income +sequence + +$$ +y_t = \begin{cases} 1 & t = 0, 1, \ldots, 45 \\ 0 & t = 46, \ldots, 65 \end{cases} +$$ + +and the default model parameters. + +a. Plot all four consumption paths on a single graph and describe their shapes + relative to one another. + +b. Plot $c_0$ against $a_0$, compute the slope of the resulting line, and verify + that the slope equals the annuity factor + $\left(\frac{1-R^{-1}}{1-R^{-(T+1)}}\right)$ from equation {eq}`eq:conssmoothing`. +``` + +```{solution-start} consmooth_ex3 +:class: dropdown +``` + +```{code-cell} ipython3 +cs_model = create_consumption_smoothing_model() +T = cs_model.T +y_seq = np.concatenate([np.ones(46), np.zeros(20)]) + +a0_vals = [-4, -2, 0, 2] +c0_vals = [] + +fig, axes = plt.subplots(1, 2, figsize=(12, 4)) + +for a0 in a0_vals: + c_seq, a_seq, h0 = compute_optimal(cs_model, a0, y_seq) + c0_vals.append(c_seq[0]) + axes[0].plot(range(T+1), c_seq, label=f'$a_0 = {a0}$') + +axes[0].set_xlabel('$t$') +axes[0].set_ylabel('$c_t$') +axes[0].set_title('Consumption paths for different $a_0$') +axes[0].legend() + +axes[1].plot(a0_vals, c0_vals, 'o-') +axes[1].set_xlabel('$a_0$') +axes[1].set_ylabel('$c_0$') +axes[1].set_title('Optimal $c_0$ as a function of $a_0$') + +plt.tight_layout() +plt.show() + +# Verify slope = annuity factor +R = cs_model.R +slope = (c0_vals[-1] - c0_vals[0]) / (a0_vals[-1] - a0_vals[0]) +annuity = (1 - 1/R) / (1 - (1/R)**(T+1)) +print(f'Numerical slope of c0 w.r.t. a0: {slope:.8f}') +print(f'Annuity factor (1 - R**(-1))/(1 - R**(-(T+1))): {annuity:.8f}') +print(f'Match: {np.isclose(slope, annuity)}') +``` + +The four paths are parallel horizontal lines, all flat but shifted vertically. + +The slope of $c_0$ with respect to $a_0$ exactly equals the annuity factor, +confirming that an extra dollar of initial wealth is spread evenly over all +$T+1$ periods as a constant additional flow. + +```{solution-end} +``` + +```{exercise} +:label: consmooth_ex4 + +The variational argument in this lecture says that the constant consumption path +maximizes welfare {eq}`welfare` among all budget-feasible paths. + +Using `compute_variation` with $\xi_1 = 0.1$ and the Experiment 1 income sequence +($W_0 = 2.5$ windfall at $t=21$, with $a_0 = -2$): + +a. Compute welfare for the optimal flat path and for variations with $\phi \in \{0.7,\, 0.9,\, 0.98,\, 1.02,\, 1.1\}$. + + Print the results in a table. + +b. Plot welfare as a function of $\phi$ on a fine grid in $[0.7, 1.1]$ and mark + the welfare of the optimal flat path as a dashed horizontal line to confirm + that it lies above these budget-feasible variations. +``` + +```{solution-start} consmooth_ex4 +:class: dropdown +``` + +```{code-cell} ipython3 +a0 = -2 +y_seq_pos = np.concatenate( + [np.ones(21), np.array([2.5]), np.ones(24), np.zeros(20)]) + +c_opt, _, _ = compute_optimal(cs_model, a0, y_seq_pos) +w_opt = welfare(cs_model, c_opt) + +print(f'Optimal (flat) welfare: {w_opt:.6f}\n') + +ϕ_vals = [0.7, 0.9, 0.98, 1.02, 1.1] +print(f'{"ϕ":>6} | {"welfare":>12} | {"vs. optimal":>14}') +print('-' * 38) +for ϕ in ϕ_vals: + cvar = compute_variation(cs_model, ξ1=0.1, ϕ=ϕ, a0=a0, + y_seq=y_seq_pos, verbose=0) + w = welfare(cs_model, cvar) + print(f'{ϕ:>6.2f} | {w:>12.6f} | {w - w_opt:>+14.6f}') + +# Fine grid +ϕ_grid = np.linspace(0.7, 1.1, 200) +w_grid = np.array([ + welfare(cs_model, + compute_variation(cs_model, ξ1=0.1, ϕ=ϕ, a0=a0, + y_seq=y_seq_pos, verbose=0)) + for ϕ in ϕ_grid +]) + +fig, ax = plt.subplots() +ax.plot(ϕ_grid, w_grid, label='Welfare of variation') +ax.axhline(w_opt, linestyle='--', color='red', label='Optimal flat path') +ax.set_xlabel(r'$\phi$') +ax.set_ylabel('Welfare') +ax.set_title('Welfare of consumption path variations ($\\xi_1 = 0.1$)') +ax.legend() +plt.show() +``` + +Every non-zero variation in the plotted family delivers strictly lower welfare than the flat path marked by the horizontal dashed line. + +The wider interval $(0.5, 1.5)$ is not informative here because values of $\phi$ well above one make the late-life variation $\xi_1\phi^t$ very large. + +This numerically confirms the variational principle that the constant consumption path is the global welfare maximizer when $\beta R = 1$. + +```{solution-end} +``` diff --git a/lectures/eigen_I.md b/lectures/eigen_I.md index 04a221ce8..46b93d508 100644 --- a/lectures/eigen_I.md +++ b/lectures/eigen_I.md @@ -14,7 +14,7 @@ kernelspec: +++ {"user_expressions": []} (eigen)= -# Eigenvalues and Eigenvectors +# Eigenvalues and Eigenvectors ```{index} single: Eigenvalues and Eigenvectors ``` @@ -23,7 +23,7 @@ kernelspec: Eigenvalues and eigenvectors are a relatively advanced topic in linear algebra. -At the same time, these concepts are extremely useful for +At the same time, these concepts are extremely useful for * economic modeling (especially dynamics!) * statistics @@ -75,7 +75,7 @@ Because $A$ is $n \times m$, it transforms $m$-vectors to $n$-vectors. We can write this formally as $A \colon \mathbb{R}^m \rightarrow \mathbb{R}^n$. -You might argue that if $A$ is a function then we should write +You might argue that if $A$ is a function then we should write $A(x) = y$ rather than $Ax = y$ but the second notation is more conventional. ### Square matrices @@ -110,8 +110,8 @@ $$ Here, the matrix $$ - A = \begin{bmatrix} 2 & 1 \\ - -1 & 1 + A = \begin{bmatrix} 2 & 1 \\ + -1 & 1 \end{bmatrix} $$ @@ -168,7 +168,7 @@ plt.show() +++ {"user_expressions": []} -One way to understand this transformation is that $A$ +One way to understand this transformation is that $A$ * first rotates $x$ by some angle $\theta$ and * then scales it by some scalar $\gamma$ to obtain the image $y$ of $x$. @@ -182,9 +182,9 @@ Let's examine some standard transformations we can perform with matrices. Below we visualize transformations by thinking of vectors as points instead of arrows. -We consider how a given matrix transforms +We consider how a given matrix transforms -* a grid of points and +* a grid of points and * a set of points located on the unit circle in $\mathbb{R}^2$. To build the transformations we will use two functions, called `grid_transform` and `circle_transform`. @@ -222,13 +222,11 @@ def grid_transform(A=np.array([[1, -1], [1, 1]])): # Plot x-y grid points ax[0].scatter(xygrid[0], xygrid[1], s=36, c=colors, edgecolor="none") - # ax[0].grid(True) # ax[0].axis("equal") ax[0].set_title("points $x_1, x_2, \cdots, x_k$") # Plot transformed grid points ax[1].scatter(uvgrid[0], uvgrid[1], s=36, c=colors, edgecolor="none") - # ax[1].grid(True) # ax[1].axis("equal") ax[1].set_title("points $Ax_1, Ax_2, \cdots, Ax_k$") @@ -286,12 +284,12 @@ def circle_transform(A=np.array([[-1, 2], [0, 1]])): ### Scaling -A matrix of the form +A matrix of the form $$ - \begin{bmatrix} - \alpha & 0 - \\ 0 & \beta + \begin{bmatrix} + \alpha & 0 + \\ 0 & \beta \end{bmatrix} $$ @@ -311,14 +309,14 @@ circle_transform(A) ### Shearing -A "shear" matrix of the form +A "shear" matrix of the form $$ - \begin{bmatrix} - 1 & \lambda \\ - 0 & 1 + \begin{bmatrix} + 1 & \lambda \\ + 0 & 1 \end{bmatrix} -$$ +$$ stretches vectors along the x-axis by an amount proportional to the y-coordinate of a point. @@ -334,12 +332,12 @@ circle_transform(A) ### Rotation -A matrix of the form +A matrix of the form $$ - \begin{bmatrix} - \cos \theta & \sin \theta - \\ - \sin \theta & \cos \theta + \begin{bmatrix} + \cos \theta & \sin \theta + \\ - \sin \theta & \cos \theta \end{bmatrix} $$ is called a _rotation matrix_. @@ -357,14 +355,14 @@ grid_transform(A) ### Permutation -The permutation matrix +The permutation matrix $$ - \begin{bmatrix} - 0 & 1 \\ - 1 & 0 + \begin{bmatrix} + 0 & 1 \\ + 1 & 0 \end{bmatrix} -$$ +$$ interchanges the coordinates of a vector. ```{code-cell} ipython3 @@ -379,26 +377,26 @@ More examples of common transition matrices can be found [here](https://en.wikip ## Matrix multiplication as composition Since matrices act as functions that transform one vector to another, we can -apply the concept of function composition to matrices as well. +apply the concept of function composition to matrices as well. ### Linear compositions -Consider the two matrices +Consider the two matrices $$ - A = - \begin{bmatrix} - 0 & 1 \\ - -1 & 0 + A = + \begin{bmatrix} + 0 & 1 \\ + -1 & 0 \end{bmatrix} \quad \text{and} \quad - B = - \begin{bmatrix} - 1 & 2 \\ - 0 & 1 + B = + \begin{bmatrix} + 1 & 2 \\ + 0 & 1 \end{bmatrix} -$$ +$$ What will the output be when we try to obtain $ABx$ for some $2 \times 1$ vector $x$? @@ -590,7 +588,7 @@ analyzing behavior where we repeatedly apply a fixed matrix. For example, given a vector $v$ and a matrix $A$, we are interested in studying the sequence -$$ +$$ v, \quad Av, \quad AAv = A^2v, \quad \ldots @@ -674,7 +672,7 @@ plot_series(B, v, n) +++ {"user_expressions": []} -Here with each iteration vectors do not tend to get longer or shorter. +Here with each iteration vectors do not tend to get longer or shorter. In this case, repeatedly multiplying a vector by $A$ simply "rotates it around an ellipse". @@ -692,7 +690,7 @@ plot_series(B, v, n) +++ {"user_expressions": []} Here with each iteration vectors tend to get longer, i.e., farther from the -origin. +origin. In this case, repeatedly multiplying a vector by $A$ makes the vector "spiral out". @@ -701,7 +699,7 @@ We thus observe that the sequence $(A^kv)_{k \geq 0}$ behaves differently depend We now discuss the property of A that determines this behavior. (la_eigenvalues)= -## Eigenvalues +## Eigenvalues ```{index} single: Linear Algebra; Eigenvalues ``` @@ -746,7 +744,6 @@ for spine in ['left', 'bottom']: ax.spines[spine].set_position('zero') for spine in ['right', 'top']: ax.spines[spine].set_color('none') -# ax.grid(alpha=0.4) xmin, xmax = -3, 3 ymin, ymax = -3, 3 @@ -786,7 +783,7 @@ So far our definition of eigenvalues and eigenvectors seems straightforward. There is one complication we haven't mentioned yet: -When solving $Av = \lambda v$, +When solving $Av = \lambda v$, * $\lambda$ is allowed to be a complex number and * $v$ is allowed to be an $n$-vector of complex numbers. @@ -799,7 +796,7 @@ We note some mathematical details for more advanced readers. (Other readers can skip to the next section.) -The eigenvalue equation is equivalent to $(A - \lambda I) v = 0$. +The eigenvalue equation is equivalent to $(A - \lambda I) v = 0$. This equation has a nonzero solution $v$ only when the columns of $A - \lambda I$ are linearly dependent. @@ -814,7 +811,7 @@ in $\lambda$ of degree $n$. This in turn implies the existence of $n$ solutions in the complex plane, although some might be repeated. -### Facts +### Facts Some nice facts about the eigenvalues of a square matrix $A$ are as follows: @@ -1096,8 +1093,8 @@ Try to compute the trajectory of $v$ after being transformed by $A$ for $n=4$ it ```{code-cell} ipython3 A = np.array([[1, 2], [1, 1]]) -v = (0.4, -0.4) -n = 11 +v = (2, -2) +n = 4 # Compute eigenvectors and eigenvalues eigenvalues, eigenvectors = np.linalg.eig(A) @@ -1108,7 +1105,7 @@ print(f'eigenvectors:\n {eigenvectors}') plot_series(A, v, n) ``` -The result seems to converge to the eigenvector of $A$ with the largest eigenvalue. +The trajectory begins to turn toward the eigenvector of $A$ with the largest eigenvalue. Let's use a [vector field](https://en.wikipedia.org/wiki/Vector_field) to visualize the transformation brought by A. @@ -1148,7 +1145,6 @@ plt.legend(lines, labels, loc='center left', plt.xlabel("x") plt.ylabel("y") plt.title("Convergence towards eigenvectors") -plt.grid() plt.gca().set_aspect('equal', adjustable='box') plt.show() ``` @@ -1237,7 +1233,6 @@ for i, example in enumerate(examples): ax[i].set_xlabel("x-axis") ax[i].set_ylabel("y-axis") - ax[i].grid() ax[i].set_aspect('equal', adjustable='box') fig.suptitle("Vector fields of the three matrices") diff --git a/lectures/eigen_II.md b/lectures/eigen_II.md index ce1c38b12..c383d4fb8 100644 --- a/lectures/eigen_II.md +++ b/lectures/eigen_II.md @@ -70,19 +70,19 @@ In other words, for each $i,j$ with $1 \leq i, j \leq n$, there exists a $k \geq Here are some examples to illustrate this further: $$ -A = \begin{bmatrix} 0.5 & 0.1 \\ - 0.2 & 0.2 +A = \begin{bmatrix} 0.5 & 0.1 \\ + 0.2 & 0.2 \end{bmatrix} $$ $A$ is irreducible since $a_{ij}>0$ for all $(i,j)$. $$ -B = \begin{bmatrix} 0 & 1 \\ - 1 & 0 +B = \begin{bmatrix} 0 & 1 \\ + 1 & 0 \end{bmatrix} , \quad -B^2 = \begin{bmatrix} 1 & 0 \\ +B^2 = \begin{bmatrix} 1 & 0 \\ 0 & 1 \end{bmatrix} $$ @@ -90,8 +90,8 @@ $$ $B$ is irreducible since $B + B^2$ is a matrix of ones. $$ -C = \begin{bmatrix} 1 & 0 \\ - 0 & 1 +C = \begin{bmatrix} 1 & 0 \\ + 0 & 1 \end{bmatrix} $$ @@ -117,7 +117,7 @@ Left eigenvectors will play important roles in what follows, including that of s A vector $w$ is called a left eigenvector of $A$ if $w$ is a right eigenvector of $A^\top$. -In other words, if $w$ is a left eigenvector of matrix $A$, then $A^\top w = \lambda w$, where $\lambda$ is the eigenvalue associated with the left eigenvector $v$. +In other words, if $w$ is a left eigenvector of matrix $A$, then $A^\top w = \lambda w$, where $\lambda$ is the eigenvalue associated with the left eigenvector $w$. This hints at how to compute left eigenvectors @@ -233,19 +233,19 @@ A matrix is called **primitive** if there exists a $k \in \mathbb{N}$ such that Recall the examples given in irreducible matrices: $$ -A = \begin{bmatrix} 0.5 & 0.1 \\ - 0.2 & 0.2 +A = \begin{bmatrix} 0.5 & 0.1 \\ + 0.2 & 0.2 \end{bmatrix} $$ $A$ here is also a primitive matrix since $A^k$ is everywhere positive for some $k \in \mathbb{N}$. $$ -B = \begin{bmatrix} 0 & 1 \\ - 1 & 0 +B = \begin{bmatrix} 0 & 1 \\ + 1 & 0 \end{bmatrix} , \quad -B^2 = \begin{bmatrix} 1 & 0 \\ +B^2 = \begin{bmatrix} 1 & 0 \\ 0 & 1 \end{bmatrix} $$ @@ -257,7 +257,7 @@ We can see that if a matrix is primitive, then it implies the matrix is irreduci Now let's step back to the primitive matrices part of the Perron-Frobenius theorem -```{prf:Theorem} Continous of Perron-Frobenius Theorem +```{prf:Theorem} Continuity of Perron-Frobenius Theorem :label: con-perron-frobenius If $A$ is primitive then, diff --git a/lectures/equalizing_difference.md b/lectures/equalizing_difference.md index 95a6635fe..b9622c5b9 100644 --- a/lectures/equalizing_difference.md +++ b/lectures/equalizing_difference.md @@ -20,11 +20,11 @@ This lecture presents a model of the college-high-school wage gap in which the Milton Friedman invented the model to study whether differences in earnings of US dentists and doctors were outcomes of competitive labor markets or whether -they reflected entry barriers imposed by governments working in conjunction with doctors' professional organizations. +they reflected entry barriers imposed by governments working in conjunction with doctors' professional organizations. Chapter 4 of Jennifer Burns {cite}`Burns_2023` describes Milton Friedman's joint work with Simon Kuznets that eventually led to the publication of {cite}`kuznets1939incomes` and {cite}`friedman1954incomes`. -To map Friedman's application into our model, think of our high school students as Friedman's dentists and our college graduates as Friedman's doctors. +To map Friedman's application into our model, think of our high school students as Friedman's dentists and our college graduates as Friedman's doctors. Our presentation is "incomplete" in the sense that it is based on a single equation that would be part of set equilibrium conditions of a more fully articulated model. @@ -35,11 +35,11 @@ The idea is that lifetime earnings somehow adjust to make a new high school wor (The job of the "other equations" in a more complete model would be to describe what adjusts to bring about this outcome.) -Our model is just one example of an "equalizing difference" theory of relative wage rates, a class of theories dating back at least to Adam Smith's **Wealth of Nations** {cite}`smith2010wealth`. +Our model is just one example of an "equalizing difference" theory of relative wage rates, a class of theories dating back at least to Adam Smith's **Wealth of Nations** {cite}`smith2010wealth`. For most of this lecture, the only mathematical tools that we'll use are from linear algebra, in particular, matrix multiplication and matrix inversion. -However, near the end of the lecture, we'll use calculus just in case readers want to see how computing partial derivatives could let us present some findings more concisely. +However, near the end of the lecture, we'll use calculus just in case readers want to see how computing partial derivatives could let us present some findings more concisely. And doing that will let illustrate how good Python is at doing calculus! @@ -63,18 +63,18 @@ Let * $R > 1$ be the gross rate of return on a one-period bond * $t = 0, 1, 2, \ldots T$ denote the years that a person either works or attends college - + * $0$ denote the first period after high school that a person can work if he does not go to college - + * $T$ denote the last period that a person works - + * $w_t^h$ be the wage at time $t$ of a high school graduate - + * $w_t^c$ be the wage at time $t$ of a college graduate - + * $\gamma_h > 1$ be the (gross) rate of growth of wages of a high school graduate, so that $ w_t^h = w_0^h \gamma_h^t$ - + * $\gamma_c > 1$ be the (gross) rate of growth of wages of a college graduate, so that $ w_t^c = w_0^c \gamma_c^t$ @@ -90,10 +90,10 @@ We now compute present values that a new high school graduate earns if If someone goes to work immediately after high school and works for the $T+1$ years $t=0, 1, 2, \ldots, T$, she earns present value $$ -h_0 = \sum_{t=0}^T R^{-t} w_t^h = w_0^h \left[ \frac{1 - (R^{-1} \gamma_h)^{T+1} }{1 - R^{-1} \gamma_h } \right] \equiv w_0^h A_h +h_0 = \sum_{t=0}^T R^{-t} w_t^h = w_0^h \left[ \frac{1 - (R^{-1} \gamma_h)^{T+1} }{1 - R^{-1} \gamma_h } \right] \equiv w_0^h A_h $$ -where +where $$ A_h = \left[ \frac{1 - (R^{-1} \gamma_h)^{T+1} }{1 - R^{-1} \gamma_h } \right]. @@ -114,7 +114,7 @@ where $$ A_c = (R^{-1} \gamma_c)^4 \left[ \frac{1 - (R^{-1} \gamma_c)^{T-3} }{1 - R^{-1} \gamma_c } \right] . -$$ +$$ The present value $c_0$ is the "human wealth" at the beginning of time $0$ of someone who chooses to attend college for four years and then start to work at time $t=4$ at the wage of a college graduate. @@ -123,29 +123,29 @@ Assume that college tuition plus four years of room and board amount to $D$ and So net of monetary cost of college, the present value of attending college as of the first period after high school is -$$ +$$ c_0 - D $$ -We now formulate a pure **equalizing difference** model of the initial college-high school wage gap $\phi$ that verifies +We now formulate a pure **equalizing difference** model of the initial college-high school wage gap $\phi$ that verifies $$ -w_0^c = \phi w_0^h +w_0^c = \phi w_0^h $$ -We suppose that $R, \gamma_h, \gamma_c, T$ and also $w_0^h$ are fixed parameters. +We suppose that $R, \gamma_h, \gamma_c, T$ and also $w_0^h$ are fixed parameters. -We start by noting that the pure equalizing difference model asserts that the college-high-school wage gap $\phi$ solves an +We start by noting that the pure equalizing difference model asserts that the college-high-school wage gap $\phi$ solves an "equalizing" equation that sets the present value not going to college equal to the present value of going to college: $$ h_0 = c_0 - D -$$ +$$ or -$$ +$$ w_0^h A_h = \phi w_0^h A_c - D . $$ (eq:equalize) @@ -159,12 +159,12 @@ $$ (eq:wagepremium) In a **free college** special case $D =0$. -Here the only cost of going to college is the forgone earnings from being a high school educated worker. +Here the only cost of going to college is the forgone earnings from being a high school educated worker. In that case, $$ -\phi = \frac{A_h}{A_c} . +\phi = \frac{A_h}{A_c} . $$ In the next section we'll write Python code to compute $\phi$ and plot it as a function of its determinants. @@ -188,16 +188,16 @@ def create_edm(R=1.05, # gross rate of return w_h0=1, # initial wage (high school) D=10, # cost for college ): - + return EqDiffModel(R, T, γ_h, γ_c, w_h0, D) def compute_gap(model): R, T, γ_h, γ_c, w_h0, D = model - + A_h = (1 - (γ_h/R)**(T+1)) / (1 - γ_h/R) A_c = (1 - (γ_c/R)**(T-3)) / (1 - γ_c/R) * (γ_c/R)**4 ϕ = A_h / A_c + D / (w_h0 * A_c) - + return ϕ ``` @@ -226,7 +226,7 @@ gap2 = compute_gap(ex2) gap2 ``` -Let us construct some graphs that show us how the initial college-high-school wage ratio $\phi$ would change if one of its determinants were to change. +Let us construct some graphs that show us how the initial college-high-school wage ratio $\phi$ would change if one of its determinants were to change. Let's start with the gross interest rate $R$. @@ -243,7 +243,7 @@ plt.show() Evidently, the initial wage ratio $\phi$ must rise to compensate a prospective high school student for **waiting** to start receiving income -- remember that while she is earning nothing in years $t=0, 1, 2, 3$, the high school worker is earning a salary. -Not let's study what happens to the initial wage ratio $\phi$ if the rate of growth of college wages rises, holding constant other +Not let's study what happens to the initial wage ratio $\phi$ if the rate of growth of college wages rises, holding constant other determinants of $\phi$. ```{code-cell} ipython3 @@ -257,11 +257,11 @@ plt.ylabel(r'wage gap') plt.show() ``` -Notice how the initial wage gap falls when the rate of growth $\gamma_c$ of college wages rises. +Notice how the initial wage gap falls when the rate of growth $\gamma_c$ of college wages rises. The wage gap falls to "equalize" the present values of the two types of career, one as a high school worker, the other as a college worker. -Can you guess what happens to the initial wage ratio $\phi$ when next we vary the rate of growth of high school wages, holding all other determinants of $\phi$ constant? +Can you guess what happens to the initial wage ratio $\phi$ when next we vary the rate of growth of high school wages, holding all other determinants of $\phi$ constant? The following graph shows what happens. @@ -290,15 +290,15 @@ $$ where $\pi \in (0,1) $ is the probability that an entrepreneur's "project" succeeds. -For our model of workers and firms, we'll interpret $D$ as the cost of becoming an entrepreneur. +For our model of workers and firms, we'll interpret $D$ as the cost of becoming an entrepreneur. -This cost might include costs of hiring workers, office space, and lawyers. +This cost might include costs of hiring workers, office space, and lawyers. What we used to call the college, high school wage gap $\phi$ now becomes the ratio -of a successful entrepreneur's earnings to a worker's earnings. +of a successful entrepreneur's earnings to a worker's earnings. We'll find that as $\pi$ decreases, $\phi$ increases, indicating that the riskier it is to -be an entrepreneur, the higher must be the reward for a successful project. +be an entrepreneur, the higher must be the reward for a successful project. Now let's adopt the entrepreneur-worker interpretation of our model @@ -314,19 +314,19 @@ def create_edm_π(R=1.05, # gross rate of return D=10, # cost for college π=0 # chance of business success ): - + return EqDiffModel(R, T, γ_h, γ_c, w_h0, D, π) def compute_gap(model): R, T, γ_h, γ_c, w_h0, D, π = model - + A_h = (1 - (γ_h/R)**(T+1)) / (1 - γ_h/R) A_c = (1 - (γ_c/R)**(T-3)) / (1 - γ_c/R) * (γ_c/R)**4 - + # Incorprate chance of success A_c = π * A_c - + ϕ = A_h / A_c + D / (w_h0 * A_c) return ϕ ``` @@ -367,7 +367,7 @@ We'll do that now. A reader who doesn't know calculus could read no further and feel confident that applying linear algebra has taught us the main properties of the model. -But for a reader interested in how we can get Python to do all the hard work involved in computing partial derivatives, we'll say a few things about that now. +But for a reader interested in how we can get Python to do all the hard work involved in computing partial derivatives, we'll say a few things about that now. We'll use the Python module 'sympy' to compute partial derivatives of $\phi$ with respect to the parameters that determine it. @@ -440,7 +440,7 @@ Compute $\frac{\partial \phi}{\partial T}$ and evaluate it at default parameters ϕ_T_func(D_value, γ_h_value, γ_c_value, R_value, T_value, w_h0_value) ``` -We find that raising $T$ decreases the initial college wage premium $\phi$. +We find that raising $T$ decreases the initial college wage premium $\phi$. This is because college graduates now have longer career lengths to "pay off" the time and other costs they paid to go to college @@ -488,3 +488,188 @@ Let's compute $\frac{\partial \phi}{\partial R}$ and evaluate it numerically at ``` We find that raising the gross interest rate $R$ increases the initial college wage premium $\phi$, in line with our earlier graphical analysis. + +## Exercises + +```{exercise} +:label: eq_ex1 + +Using `compute_gap`, plot the college-high-school wage premium $\phi$ as a function +of tuition cost $D \in [0, 30]$ with all other parameters at their default values. + +a. Add a horizontal dashed line at $\phi = 1$ and explain why $\phi$ does or does + not reach 1 in this range in terms of the free-college formula $\phi = A_h / A_c$. + +b. Numerically estimate $\partial\phi/\partial D$ as the slope of the plotted + line and compare it to the symbolic derivative $\phi_D$ computed with SymPy + in this lecture. +``` + +```{solution-start} eq_ex1 +:class: dropdown +``` + +```{code-cell} ipython3 +D_arr = np.linspace(0, 30, 200) +# Use create_edm_π with π=1 (certainty) so compute_gap handles the 7-field model +models = [create_edm_π(D=d, π=1.0) for d in D_arr] +gaps = [compute_gap(m) for m in models] + +fig, ax = plt.subplots() +ax.plot(D_arr, gaps, label=r'$\phi(D)$') +ax.axhline(1, linestyle='--', color='red', label=r'$\phi = 1$') +ax.set_xlabel('$D$ (tuition cost)') +ax.set_ylabel(r'College wage premium $\phi$') +ax.set_title('College wage premium vs tuition cost') +ax.legend() +plt.show() + +# Numerical slope (finite difference) +slope_num = (gaps[-1] - gaps[0]) / (D_arr[-1] - D_arr[0]) + +# Compare with SymPy ϕ_D_func already computed in this lecture +slope_sympy = float(ϕ_D_func(D_value, γ_h_value, γ_c_value, R_value, T_value, w_h0_value)) + +print(f'Numerical dϕ/dD: {slope_num:.6f}') +print(f'SymPy dϕ/dD: {slope_sympy:.6f}') +print(f'Match: {abs(slope_num - slope_sympy) < 1e-4}') +``` + +Because $A_h > A_c$ (forgone earnings dominate even with $D=0$), the free-college +premium $A_h/A_c > 1$, so $\phi$ exceeds 1 for all $D \geq 0$. + +```{solution-end} +``` + +```{exercise} +:label: eq_ex2 + +Plot the college wage premium $\phi$ as a function of career length +$T \in \{10, 15, 20, \ldots, 60\}$ for two cases: + +1. Free college: $D = 0$. +2. Costly college: $D = 10$. + +On the same graph, plot both curves, add a horizontal dashed line at $\phi = 1$, +and explain the direction of the relationship between $T$ and $\phi$ in terms of +the present-value factors $A_h$ and $A_c$. +``` + +```{solution-start} eq_ex2 +:class: dropdown +``` + +```{code-cell} ipython3 +T_arr = np.arange(10, 65, 5) + +gaps_free = [compute_gap(create_edm_π(T=t, D=0, π=1.0)) for t in T_arr] +gaps_costly = [compute_gap(create_edm_π(T=t, D=10, π=1.0)) for t in T_arr] + +fig, ax = plt.subplots() +ax.plot(T_arr, gaps_free, 'o-', label='$D = 0$ (free college)') +ax.plot(T_arr, gaps_costly, 's-', label='$D = 10$ (costly college)') +ax.axhline(1, linestyle='--', color='gray', label=r'$\phi = 1$') +ax.set_xlabel('Career length $T$') +ax.set_ylabel(r'College wage premium $\phi$') +ax.set_title('College wage premium vs career length') +ax.legend() +plt.show() +``` + +As $T$ rises, college graduates have more years over which to "recoup" the cost +of their four-year delay in starting work because $A_c$ grows faster than $A_h$ +when the 4-year discount factor $(R^{-1}\gamma_c)^4$ is amortised over more periods. + +This shrinks $A_h/A_c$ and therefore $\phi$. + +```{solution-end} +``` + +```{exercise} +:label: eq_ex3 + +Verify the SymPy partial derivative $\partial\phi/\partial R$ numerically using a +**central finite-difference** approximation + +$$ +\frac{\partial\phi}{\partial R}\bigg|_{R=R_0} \approx +\frac{\phi(R_0 + \varepsilon) - \phi(R_0 - \varepsilon)}{2\varepsilon} +$$ + +for $\varepsilon = 10^{-5}$, evaluating at the default parameter values and +comparing with the symbolic result computed in this lecture. +``` + +```{solution-start} eq_ex3 +:class: dropdown +``` + +```{code-cell} ipython3 +ε = 1e-5 + +# Finite-difference estimate using create_edm_π (π=1 for the standard model) +gap_plus = compute_gap(create_edm_π(R=R_value + ε, π=1.0)) +gap_minus = compute_gap(create_edm_π(R=R_value - ε, π=1.0)) +dϕ_dR_fd = (gap_plus - gap_minus) / (2 * ε) + +# SymPy result from ϕ_R_func already computed in this lecture +dϕ_dR_sym = float(ϕ_R_func(D_value, γ_h_value, γ_c_value, R_value, T_value, w_h0_value)) + +print(f'Finite-difference dϕ/dR: {dϕ_dR_fd:.6f}') +print(f'SymPy dϕ/dR: {dϕ_dR_sym:.6f}') +print(f'Absolute error: {abs(dϕ_dR_fd - dϕ_dR_sym):.2e}') +``` + +The two estimates agree to at least five significant figures, confirming that the +symbolic calculus and numerical computation are consistent. + +```{solution-end} +``` + +```{exercise} +:label: eq_ex4 + +Using the entrepreneur-worker version of the model (`create_edm_π`), answer the +following questions. + +a. Plot the required wage premium $\phi$ for a successful entrepreneur as a + function of the success probability $\pi \in [0.10, 1.00]$ and mark the + horizontal line $\phi = 2$ as a dashed line. + +b. At what approximate value of $\pi$ does the premium cross 2, and for which side of that threshold is the premium above 2? + +c. Explain intuitively why the premium rises as $\pi \to 0$. +``` + +```{solution-start} eq_ex4 +:class: dropdown +``` + +```{code-cell} ipython3 +π_arr = np.linspace(0.10, 1.00, 200) +# create_edm_π and compute_gap are already defined in this lecture +ϕ_arr_π = np.array([compute_gap(create_edm_π(π=p)) for p in π_arr]) + +fig, ax = plt.subplots() +ax.plot(π_arr, ϕ_arr_π, label=r'$\phi(\pi)$') +ax.axhline(2, linestyle='--', color='red', label=r'$\phi = 2$') +ax.set_xlabel(r'Success probability $\pi$') +ax.set_ylabel(r'Required wage premium $\phi$') +ax.set_title('Entrepreneur premium vs success probability') +ax.legend() +plt.show() + +# Interpolate the crossing on the decreasing curve ϕ(π) +crossing = np.interp(2, ϕ_arr_π[::-1], π_arr[::-1]) +above_idx = np.where(ϕ_arr_π > 2)[0] + +print(f'Premium equals 2 at π approximately {crossing:.3f}') +print(f'On the grid, premium exceeds 2 for π below about {π_arr[above_idx[-1]]:.3f}') +``` + +As $\pi \to 0$ the expected lifetime earnings of an entrepreneur approach zero +regardless of $\phi$, so $\phi$ must rise without bound to keep the +entrepreneur indifferent between entrepreneurship and wage work. + +```{solution-end} +``` diff --git a/lectures/french_rev.md b/lectures/french_rev.md index da2bb018d..bbd8a961f 100644 --- a/lectures/french_rev.md +++ b/lectures/french_rev.md @@ -11,14 +11,14 @@ kernelspec: name: python3 --- -# Inflation During French Revolution +# Inflation During French Revolution -## Overview +## Overview This lecture describes some of the monetary and fiscal features of the French Revolution (1789-1799) described by {cite}`sargent_velde1995`. -To finance public expenditures and service its debts, +To finance public expenditures and service its debts, the French government embarked on policy experiments. The authors of these experiments had in mind theories about how government monetary and fiscal policies affected economic outcomes. @@ -30,29 +30,29 @@ Some of those theories about monetary and fiscal policies still interest us toda * this normative (i.e., prescriptive model) advises a government to finance temporary war-time surges in expenditures mostly by issuing government debt, raising taxes by just enough to service the additional debt issued during the wary; then, after the war, to roll over whatever debt the government had accumulated during the war; and to increase taxes after the war permanently by just enough to finance interest payments on that post-war government debt * **unpleasant monetarist arithmetic** like that described in this quanteon lecture {doc}`unpleasant` - - * mathematics involving compound interest governed French government debt dynamics in the decades preceding 1789; according to leading historians, that arithmetic set the stage for the French Revolution + + * mathematics involving compound interest governed French government debt dynamics in the decades preceding 1789; according to leading historians, that arithmetic set the stage for the French Revolution * a *real bills* theory of the effects of government open market operations in which the government *backs* new issues of paper money with government holdings of valuable real property or financial assets that holders of money can purchase from the government in exchange for their money. * The Revolutionaries learned about this theory from Adam Smith's 1776 book The Wealth of Nations {cite}`smith2010wealth` and other contemporary sources - * It shaped how the Revolutionaries issued a paper money called **assignats** from 1789 to 1791 + * It shaped how the Revolutionaries issued a paper money called **assignats** from 1789 to 1791 * a classical **gold** or **silver standard** - + * Napoleon Bonaparte became head of the French government in 1799. He used this theory to guide his monetary and fiscal policies * a classical **inflation-tax** theory of inflation in which Philip Cagan's ({cite}`Cagan`) demand for money studied in this lecture {doc}`cagan_ree` is a key component - * This theory helps explain French price level and money supply data from 1794 to 1797 + * This theory helps explain French price level and money supply data from 1794 to 1797 + +* a **legal restrictions** or **financial repression** theory of the demand for real balances -* a **legal restrictions** or **financial repression** theory of the demand for real balances - - * The Twelve Members comprising the Committee of Public Safety who adminstered the Terror from June 1793 to July 1794 used this theory to shape their monetary policy + * The Twelve Members comprising the Committee of Public Safety who adminstered the Terror from June 1793 to July 1794 used this theory to shape their monetary policy -We use matplotlib to replicate several of the graphs with which {cite}`sargent_velde1995` portrayed outcomes of these experiments +We use matplotlib to replicate several of the graphs with which {cite}`sargent_velde1995` portrayed outcomes of these experiments ## Data Sources @@ -98,15 +98,15 @@ mystnb: name: fr_fig4 --- # Read the data from Excel file -data2 = pd.read_excel(dette_url, - sheet_name='Militspe', usecols='M:X', +data2 = pd.read_excel(dette_url, + sheet_name='Militspe', usecols='M:X', skiprows=7, nrows=102, header=None) # French military spending, 1685-1789, in 1726 livres -data4 = pd.read_excel(dette_url, - sheet_name='Militspe', usecols='D', +data4 = pd.read_excel(dette_url, + sheet_name='Militspe', usecols='D', skiprows=3, nrows=105, header=None).squeeze() - + years = range(1685, 1790) plt.figure() @@ -133,7 +133,7 @@ Britain won the first three wars and lost the fourth. Each of those wars produced surges in both countries' government expenditures that each country somehow had to finance. Figure {numref}`fr_fig4` shows surges in military expenditures in France (in blue) and Great Britain. -during those four wars. +during those four wars. A remarkable aspect of figure {numref}`fr_fig4` is that despite having a population less than half of France's, Britain was able to finance military expenses of about the same amounts as France's. @@ -147,7 +147,7 @@ mystnb: name: fr_fig2 --- # Read the data from Excel file -data2 = pd.read_excel(dette_url, sheet_name='Militspe', usecols='M:X', +data2 = pd.read_excel(dette_url, sheet_name='Militspe', usecols='M:X', skiprows=7, nrows=102, header=None) # Plot the data @@ -155,7 +155,7 @@ plt.figure() plt.plot(range(1689, 1791), data2.iloc[:, 5], linewidth=0.8) plt.plot(range(1689, 1791), data2.iloc[:, 11], linewidth=0.8, color='red') plt.plot(range(1689, 1791), data2.iloc[:, 9], linewidth=0.8, color='orange') -plt.plot(range(1689, 1791), data2.iloc[:, 8], 'o-', +plt.plot(range(1689, 1791), data2.iloc[:, 8], 'o-', markerfacecolor='none', linewidth=0.8, color='purple') # Customize the plot @@ -180,11 +180,11 @@ Figures {numref}`fr_fig2` and {numref}`fr_fig3` summarize British and French g Before 1789, progressive forces in France admired how Britain had financed its government expenditures and wanted to redesign French fiscal arrangements to make them more like Britain's. -Figure {numref}`fr_fig2` shows government expenditures and how it was distributed among expenditures for +Figure {numref}`fr_fig2` shows government expenditures and how it was distributed among expenditures for * civil (non-military) activities - * debt service, i.e., interest payments - * military expenditures (the yellow line minus the red line) + * debt service, i.e., interest payments + * military expenditures (the yellow line minus the red line) Figure {numref}`fr_fig2` also plots total government revenues from tax collections (the purple circled line) @@ -205,9 +205,9 @@ Figure {numref}`fr_fig2` indicates that * thus, after a war, the government does *not* raise taxes by enough to pay off its debt * instead, it just rolls over whatever debt it inherits, raising taxes by just enough to service the interest payments on that debt -Eighteenth-century British fiscal policy portrayed Figure {numref}`fr_fig2` thus looks very much like a text-book example of a *tax-smoothing* model like Robert Barro's {cite}`Barro1979`. +Eighteenth-century British fiscal policy portrayed Figure {numref}`fr_fig2` thus looks very much like a text-book example of a *tax-smoothing* model like Robert Barro's {cite}`Barro1979`. -A striking feature of the graph is what we'll label a *law of gravity* between tax collections and government expenditures. +A striking feature of the graph is what we'll label a *law of gravity* between tax collections and government expenditures. * levels of government expenditures at taxes attract each other * while they can temporarily differ -- as they do during wars -- they come back together when peace returns @@ -224,9 +224,9 @@ mystnb: name: fr_fig1 --- # Read the data from the Excel file -data1 = pd.read_excel(dette_url, sheet_name='Debt', +data1 = pd.read_excel(dette_url, sheet_name='Debt', usecols='R:S', skiprows=5, nrows=99, header=None) -data1a = pd.read_excel(dette_url, sheet_name='Debt', +data1a = pd.read_excel(dette_url, sheet_name='Debt', usecols='P', skiprows=89, nrows=15, header=None) # Plot the data @@ -235,7 +235,7 @@ plt.plot(range(1690, 1789), 100 * data1.iloc[:, 1], linewidth=0.8) date = np.arange(1690, 1789) index = (date < 1774) & (data1.iloc[:, 0] > 0) -plt.plot(date[index], 100 * data1[index].iloc[:, 0], +plt.plot(date[index], 100 * data1[index].iloc[:, 0], '*:', color='r', linewidth=0.8) # Plot the additional data @@ -254,15 +254,15 @@ plt.tight_layout() plt.show() ``` -Figure {numref}`fr_fig1` shows that interest payments on government debt (i.e., so-called ''debt service'') were high fractions of government tax revenues in both Great Britain and France. +Figure {numref}`fr_fig1` shows that interest payments on government debt (i.e., so-called ''debt service'') were high fractions of government tax revenues in both Great Britain and France. -{numref}`fr_fig2` showed us that in peace times Britain managed to balance its budget despite those large interest costs. +{numref}`fr_fig2` showed us that in peace times Britain managed to balance its budget despite those large interest costs. But as we'll see in our next graph, on the eve of the French Revolution in 1788, the fiscal *law of gravity* that worked so well in Britain did not working very well in France. ```{code-cell} ipython3 # Read the data from the Excel file -data1 = pd.read_excel(fig_3_url, sheet_name='Sheet1', +data1 = pd.read_excel(fig_3_url, sheet_name='Sheet1', usecols='C:F', skiprows=5, nrows=30, header=None) data1.replace(0, np.nan, inplace=True) @@ -280,7 +280,7 @@ plt.figure() plt.plot(range(1759, 1789, 1), data1.iloc[:, 0], '-x', linewidth=0.8) plt.plot(range(1759, 1789, 1), data1.iloc[:, 1], '--*', linewidth=0.8) -plt.plot(range(1759, 1789, 1), data1.iloc[:, 2], +plt.plot(range(1759, 1789, 1), data1.iloc[:, 2], '-o', linewidth=0.8, markerfacecolor='none') plt.plot(range(1759, 1789, 1), data1.iloc[:, 3], '-*', linewidth=0.8) @@ -299,13 +299,13 @@ plt.tight_layout() plt.show() ``` -{numref}`fr_fig3` shows that on the eve of the French Revolution in 1788, government expenditures exceeded tax revenues. +{numref}`fr_fig3` shows that on the eve of the French Revolution in 1788, government expenditures exceeded tax revenues. -Especially during and after France's expenditures to help the Americans in their War of Independence from Great Britain, growing government debt service (i.e., interest payments) -contributed to this situation. +Especially during and after France's expenditures to help the Americans in their War of Independence from Great Britain, growing government debt service (i.e., interest payments) +contributed to this situation. -This was partly a consequence of the unfolding of the debt dynamics that underlies the Unpleasant Arithmetic discussed in this quantecon lecture {doc}`unpleasant`. +This was partly a consequence of the unfolding of the debt dynamics that underlies the Unpleasant Arithmetic discussed in this quantecon lecture {doc}`unpleasant`. {cite}`sargent_velde1995` describe how the Ancient Regime that until 1788 had governed France had stable institutional features that made it difficult for the government to balance its budget. @@ -314,7 +314,7 @@ Powerful contending interests had prevented from the government from closing the total expenditures and its tax revenues by either * raising taxes, or - * lowering government's non-debt service (i.e., non-interest) expenditures, or + * lowering government's non-debt service (i.e., non-interest) expenditures, or * lowering debt service (i.e., interest) costs by rescheduling, i.e., defaulting on some debts Precedents and prevailing French arrangements had empowered three constituencies to block adjustments to components of the government budget constraint that they cared especially about @@ -324,20 +324,20 @@ Precedents and prevailing French arrangements had empowered three constituencies * government creditors (i.e., owners of government bonds) When the French government had confronted a similar situation around 1720 after King Louis XIV's -Wars had left it with a debt crisis, it had sacrificed the interests of +Wars had left it with a debt crisis, it had sacrificed the interests of government creditors, i.e., by defaulting enough of its debt to bring reduce interest payments down enough to balance the budget. Somehow, in 1789, creditors of the French government were more powerful than they had been in 1720. Therefore, King Louis XVI convened the Estates General together to ask them to redesign the French constitution in a way that would lower government expenditures or increase taxes, thereby -allowing him to balance the budget while also honoring his promises to creditors of the French government. +allowing him to balance the budget while also honoring his promises to creditors of the French government. The King called the Estates General together in an effort to promote the reforms that would -would bring sustained budget balance. +would bring sustained budget balance. {cite}`sargent_velde1995` describe how the French Revolutionaries set out to accomplish that. -## Nationalization, Privatization, Debt Reduction +## Nationalization, Privatization, Debt Reduction In 1789, the Revolutionaries quickly reorganized the Estates General into a National Assembly. @@ -345,53 +345,53 @@ A first piece of business was to address the fiscal crisis, the situation that h The Revolutionaries were not socialists or communists. -To the contrary, they respected private property and knew state-of-the-art economics. +To the contrary, they respected private property and knew state-of-the-art economics. They knew that to honor government debts, they would have to raise new revenues or reduce expenditures. -A coincidence was that the Catholic Church owned vast income-producing properties. +A coincidence was that the Catholic Church owned vast income-producing properties. -Indeed, the capitalized value of those income streams put estimates of the value of church lands at -about the same amount as the entire French government debt. +Indeed, the capitalized value of those income streams put estimates of the value of church lands at +about the same amount as the entire French government debt. This coincidence fostered a three step plan for servicing the French government debt * nationalize the church lands -- i.e., sequester or confiscate it without paying for it - * sell the church lands + * sell the church lands * use the proceeds from those sales to service or even retire French government debt The monetary theory underlying this plan had been set out by Adam Smith in his analysis of what he called *real bills* in his 1776 book **The Wealth of Nations** {cite}`smith2010wealth`, which many of the revolutionaries had read. -Adam Smith defined a *real bill* as a paper money note that is backed by a claims on a real asset like productive capital or inventories. +Adam Smith defined a *real bill* as a paper money note that is backed by a claims on a real asset like productive capital or inventories. The National Assembly put together an ingenious institutional arrangement to implement this plan. In response to a motion by Catholic Bishop Talleyrand (an atheist), -the National Assembly confiscated and nationalized Church lands. +the National Assembly confiscated and nationalized Church lands. The National Assembly intended to use earnings from Church lands to service its national debt. To do this, it began to implement a ''privatization plan'' that would let it service its debt while not raising taxes. -Their plan involved issuing paper notes called ''assignats'' that entitled bearers to use them to purchase state lands. +Their plan involved issuing paper notes called ''assignats'' that entitled bearers to use them to purchase state lands. -These paper notes would be ''as good as silver coins'' in the sense that both were acceptable means of payment in exchange for those (formerly) church lands. +These paper notes would be ''as good as silver coins'' in the sense that both were acceptable means of payment in exchange for those (formerly) church lands. Finance Minister Necker and the Constituents of the National Assembly thus planned to solve the privatization problem *and* the debt problem simultaneously -by creating a new currency. +by creating a new currency. They devised a scheme to raise revenues by auctioning the confiscated lands, thereby withdrawing paper notes issued on the security of the lands sold by the government. This ''tax-backed money'' scheme propelled the National Assembly into the domains of then modern monetary theories. - + Records of debates show how members of the Assembly marshaled theory and evidence to assess the likely -effects of their innovation. +effects of their innovation. * Members of the National Assembly quoted David Hume and Adam Smith * They cited John Law's System of 1720 and the American experiences with paper money fifteen years @@ -428,7 +428,7 @@ mystnb: name: fr_fig5 --- # Read data from Excel file -data5 = pd.read_excel(dette_url, sheet_name='Debt', usecols='K', +data5 = pd.read_excel(dette_url, sheet_name='Debt', usecols='K', skiprows=41, nrows=120, header=None) # Plot the data @@ -451,7 +451,7 @@ until after 1815, when Napoleon Bonaparte was exiled to St Helena and King Louis * from 1799 to 1814, Napoleon Bonaparte had other sources of revenues -- booty and reparations from provinces and nations that he defeated in war - * from 1789 to 1799, the French Revolutionaries turned to another source to raise resources to pay for government purchases of goods and services and to service French government debt. + * from 1789 to 1799, the French Revolutionaries turned to another source to raise resources to pay for government purchases of goods and services and to service French government debt. And as the next figure shows, government expenditures exceeded tax revenues by substantial amounts during the period form 1789 to 1799. @@ -497,9 +497,9 @@ plt.tight_layout() plt.show() ``` -To cover the discrepancies between government expenditures and tax revenues revealed in {numref}`fr_fig11`, the French revolutionaries printed paper money and spent it. +To cover the discrepancies between government expenditures and tax revenues revealed in {numref}`fr_fig11`, the French revolutionaries printed paper money and spent it. -The next figure shows that by printing money, they were able to finance substantial purchases +The next figure shows that by printing money, they were able to finance substantial purchases of goods and services, including military goods and soldiers' pay. ```{code-cell} ipython3 @@ -510,7 +510,7 @@ mystnb: name: fr_fig24 --- # Read data from Excel file -data12 = pd.read_excel(assignat_url, sheet_name='seignor', +data12 = pd.read_excel(assignat_url, sheet_name='seignor', usecols='F', skiprows=6, nrows=75, header=None).squeeze() # Create a figure and plot the data @@ -522,12 +522,12 @@ plt.gca().spines['top'].set_visible(False) plt.gca().spines['right'].set_visible(False) plt.axhline(y=472.42/12, color='r', linestyle=':') -plt.xticks(ticks=pd.date_range(start='1790', +plt.xticks(ticks=pd.date_range(start='1790', end='1796', freq='YS'), labels=range(1790, 1797)) plt.xlim(pd.Timestamp('1791'), pd.Timestamp('1796-02') + pd.DateOffset(months=2)) plt.ylabel('millions of livres', fontsize=12) -plt.text(pd.Timestamp('1793-11'), 39.5, 'revenues in 1788', +plt.text(pd.Timestamp('1793-11'), 39.5, 'revenues in 1788', verticalalignment='top', fontsize=12) plt.tight_layout() @@ -542,17 +542,17 @@ $$ \frac{M_{t+1} - M_t}{p_t} $$ -where +where * $M_t$ is the stock of paper money at time $t$ measured in livres * $p_t$ is the price level at time $t$ measured in units of goods per livre at time $t$ * $M_{t+1} - M_t$ is the amount of new money printed at time $t$ -Notice the 1793-1794 surge in revenues raised by printing money. +Notice the 1793-1794 surge in revenues raised by printing money. * This reflects extraordinary measures that the Committee for Public Safety adopted to force citizens to accept paper money, or else. -Also note the abrupt fall off in revenues raised by 1797 and the absence of further observations after 1797. +Also note the abrupt fall off in revenues raised by 1797 and the absence of further observations after 1797. * This reflects the end of using the printing press to raise revenues. @@ -573,9 +573,9 @@ mystnb: name: fr_fig9 --- # Read the data from Excel file -data7 = pd.read_excel(assignat_url, sheet_name='Data', +data7 = pd.read_excel(assignat_url, sheet_name='Data', usecols='P:Q', skiprows=4, nrows=80, header=None) -data7a = pd.read_excel(assignat_url, sheet_name='Data', +data7a = pd.read_excel(assignat_url, sheet_name='Data', usecols='L', skiprows=4, nrows=80, header=None) # Create the figure and plot plt.figure() @@ -605,10 +605,10 @@ plt.show() ``` We have partioned {numref}`fr_fig9` that shows the log of the price level and {numref}`fr_fig8` -below that plots real balances $\frac{M_t}{p_t}$ into three periods that correspond to different monetary experiments or *regimes*. +below that plots real balances $\frac{M_t}{p_t}$ into three periods that correspond to different monetary experiments or *regimes*. The first period ends in the late summer of 1793, and is characterized -by growing real balances and moderate inflation. +by growing real balances and moderate inflation. The second period begins and ends with the Terror. It is marked by high real balances, around 2,500 million, and @@ -621,7 +621,7 @@ these three episodes in terms of distinct theories * a *backing* or *real bills* theory (the classic text for this theory is Adam Smith {cite}`smith2010wealth`) * a legal restrictions theory ( {cite}`keynes1940pay`, {cite}`bryant1984price` ) * a classical hyperinflation theory ({cite}`Cagan`) -* +* ```{note} According to the empirical definition of hyperinflation adopted by {cite}`Cagan`, beginning in the month that inflation exceeds 50 percent @@ -642,20 +642,20 @@ mystnb: name: fr_fig8 --- # Read the data from Excel file -data7 = pd.read_excel(assignat_url, sheet_name='Data', +data7 = pd.read_excel(assignat_url, sheet_name='Data', usecols='P:Q', skiprows=4, nrows=80, header=None) -data7a = pd.read_excel(assignat_url, sheet_name='Data', +data7a = pd.read_excel(assignat_url, sheet_name='Data', usecols='L', skiprows=4, nrows=80, header=None) # Create the figure and plot plt.figure() -h = plt.plot(pd.date_range(start='1789-11-01', periods=len(data7), freq='ME'), +h = plt.plot(pd.date_range(start='1789-11-01', periods=len(data7), freq='ME'), (data7a.values * [1, 1]) * data7.values, linewidth=1.) plt.setp(h[1], linestyle='--', color='red') -plt.vlines([pd.Timestamp('1793-07-15'), pd.Timestamp('1793-07-15')], +plt.vlines([pd.Timestamp('1793-07-15'), pd.Timestamp('1793-07-15')], 0, 3000, linewidth=0.8, color='orange') -plt.vlines([pd.Timestamp('1794-07-15'), pd.Timestamp('1794-07-15')], +plt.vlines([pd.Timestamp('1794-07-15'), pd.Timestamp('1794-07-15')], 0, 3000, linewidth=0.8, color='purple') plt.ylim([0, 3000]) @@ -680,8 +680,8 @@ plt.show() The three clouds of points in Figure {numref}`fr_fig104` - depict different real balance-inflation relationships. - + depict different real balance-inflation relationships. + Only the cloud for the third period has the inverse relationship familiar to us now from twentieth-century hyperinflations. @@ -709,7 +709,7 @@ def fit(x, y): caron = np.load('datasets/caron.npy') nom_balances = np.load('datasets/nom_balances.npy') -infl = np.concatenate(([np.nan], +infl = np.concatenate(([np.nan], -np.log(caron[1:63, 1] / caron[0:62, 1]))) bal = nom_balances[14:77, 1] * caron[:, 1] / 1000 ``` @@ -738,14 +738,14 @@ plt.gca().spines['top'].set_visible(False) plt.gca().spines['right'].set_visible(False) # First subsample -plt.plot(bal[1:31], infl[1:31], 'o', markerfacecolor='none', +plt.plot(bal[1:31], infl[1:31], 'o', markerfacecolor='none', color='blue', label='real bills period') # Second subsample plt.plot(bal[31:44], infl[31:44], '+', color='red', label='terror') # Third subsample -plt.plot(bal[44:63], infl[44:63], '*', +plt.plot(bal[44:63], infl[44:63], '*', color='orange', label='classic Cagan hyperinflation') plt.xlabel('real balances') @@ -756,17 +756,17 @@ plt.tight_layout() plt.show() ``` -The three clouds of points in {numref}`fr_fig104` evidently - depict different real balance-inflation relationships. +The three clouds of points in {numref}`fr_fig104` evidently + depict different real balance-inflation relationships. Only the cloud for the third period has the inverse relationship familiar to us now from twentieth-century hyperinflations. - To bring this out, we'll use linear regressions to draw straight lines that compress the - inflation-real balance relationship for our three sub-periods. + To bring this out, we'll use linear regressions to draw straight lines that compress the + inflation-real balance relationship for our three sub-periods. - Before we do that, we'll drop some of the early observations during the terror period + Before we do that, we'll drop some of the early observations during the terror period to obtain the following graph. ```{code-cell} ipython3 @@ -824,7 +824,7 @@ plt.gca().spines['top'].set_visible(False) plt.gca().spines['right'].set_visible(False) # First subsample -plt.plot(bal[1:31], infl[1:31], 'o', markerfacecolor='none', +plt.plot(bal[1:31], infl[1:31], 'o', markerfacecolor='none', color='blue', label='real bills period') plt.plot(bal[1:31], a1 + bal[1:31] * b1, color='blue') @@ -832,7 +832,7 @@ plt.plot(bal[1:31], a1 + bal[1:31] * b1, color='blue') plt.plot(bal[31:44], infl[31:44], '+', color='red', label='terror') # Third subsample -plt.plot(bal[44:63], infl[44:63], '*', +plt.plot(bal[44:63], infl[44:63], '*', color='orange', label='classic Cagan hyperinflation') plt.xlabel('real balances') @@ -845,12 +845,12 @@ plt.show() The regression line in {numref}`fr_fig104c` shows that large increases in real balances of assignats (paper money) were accompanied by only modest rises in the price level, an outcome in line -with the *real bills* theory. +with the *real bills* theory. -During this period, assignats were claims on church lands. +During this period, assignats were claims on church lands. But towards the end of this period, the price level started to rise and real balances to fall -as the government continued to print money but stopped selling church land. +as the government continued to print money but stopped selling church land. To get people to hold that paper money, the government forced people to hold it by using legal restrictions. @@ -869,7 +869,7 @@ plt.gca().spines['top'].set_visible(False) plt.gca().spines['right'].set_visible(False) # First subsample -plt.plot(bal[1:31], infl[1:31], 'o', markerfacecolor='none', +plt.plot(bal[1:31], infl[1:31], 'o', markerfacecolor='none', color='blue', label='real bills period') # Second subsample @@ -877,7 +877,7 @@ plt.plot(bal[31:44], infl[31:44], '+', color='red', label='terror') plt.plot(a2_rev + b2_rev * infl[31:44], infl[31:44], color='red') # Third subsample -plt.plot(bal[44:63], infl[44:63], '*', +plt.plot(bal[44:63], infl[44:63], '*', color='orange', label='classic Cagan hyperinflation') plt.xlabel('real balances') @@ -889,17 +889,19 @@ plt.show() ``` The regression line in {numref}`fr_fig104d` shows that large increases in real balances of -assignats (paper money) were accompanied by little upward price level pressure, even some declines in prices. +assignats (paper money) were accompanied by little upward price level pressure, even some declines in prices. -This reflects how well legal restrictions -- financial repression -- was working during the period of the Terror. +This reflects how well legal restrictions -- financial repression -- was working during the period of the Terror. -But the Terror ended in July 1794. That unleashed a big inflation as people tried to find other ways to transact and store values. +But the Terror ended in July 1794. + +That unleashed a big inflation as people tried to find other ways to transact and store values. The following two graphs are for the classical hyperinflation period. One regresses inflation on real balances, the other regresses real balances on inflation. -Both show a prounced inverse relationship that is the hallmark of the hyperinflations studied by +Both show a prounced inverse relationship that is the hallmark of the hyperinflations studied by Cagan {cite}`Cagan`. ```{code-cell} ipython3 @@ -914,14 +916,14 @@ plt.gca().spines['top'].set_visible(False) plt.gca().spines['right'].set_visible(False) # First subsample -plt.plot(bal[1:31], infl[1:31], 'o', markerfacecolor='none', +plt.plot(bal[1:31], infl[1:31], 'o', markerfacecolor='none', color='blue', label='real bills period') # Second subsample plt.plot(bal[31:44], infl[31:44], '+', color='red', label='terror') # Third subsample -plt.plot(bal[44:63], infl[44:63], '*', +plt.plot(bal[44:63], infl[44:63], '*', color='orange', label='classic Cagan hyperinflation') plt.plot(bal[44:63], a3 + bal[44:63] * b3, color='orange') @@ -948,14 +950,14 @@ plt.gca().spines['top'].set_visible(False) plt.gca().spines['right'].set_visible(False) # First subsample -plt.plot(bal[1:31], infl[1:31], 'o', +plt.plot(bal[1:31], infl[1:31], 'o', markerfacecolor='none', color='blue', label='real bills period') # Second subsample plt.plot(bal[31:44], infl[31:44], '+', color='red', label='terror') # Third subsample -plt.plot(bal[44:63], infl[44:63], '*', +plt.plot(bal[44:63], infl[44:63], '*', color='orange', label='classic Cagan hyperinflation') plt.plot(a3_rev + b3_rev * infl[44:63], infl[44:63], color='orange') @@ -972,7 +974,7 @@ period of the hyperinflation. ## Hyperinflation Ends -{cite}`sargent_velde1995` tell how in 1797 the Revolutionary government abruptly ended the inflation by +{cite}`sargent_velde1995` tell how in 1797 the Revolutionary government abruptly ended the inflation by * repudiating 2/3 of the national debt, and thereby * eliminating the net-of-interest government defict @@ -988,3 +990,240 @@ This lecture sets the stage for studying theories of inflation and the govern A *monetarist theory of the price level* is described in this quantecon lecture {doc}`cagan_ree`. That lecture sets the stage for these quantecon lectures {doc}`money_inflation` and {doc}`unpleasant`. + +## Exercises + +```{exercise} +:label: fr_ex1 + +**Identifying the hyperinflation: Cagan's 50 per cent threshold.** + +The "Note" callout box in this lecture states that France experienced a +hyperinflation from May to December 1795, using Cagan's definition: a +hyperinflation begins in the first month that the monthly inflation rate exceeds +50 per cent and ends in the first month it stays below 50 per cent for at least +a year. + +Because `infl` measures the *log* change in the price level, the 50 per cent +threshold in simple-ratio terms ($P_{t+1}/P_t - 1 \geq 0.5$) corresponds to +$\log(1.5) \approx 0.405$ in the `infl` units. + +a. Using the approximate date grid + `pd.date_range(start='1791-01', periods=63, freq='ME')`, plot the full + monthly inflation series `infl[1:]` (skip the leading `NaN`), draw a dashed + horizontal line at the 50 per cent threshold, and shade the months that + exceed it. + +b. Print the number of months above the threshold and their approximate dates to + check whether they match the "May to December 1795" statement in the lecture. + +c. Using the lecture's boundary at index 44 (August 1794, roughly), compute how + many Cagan-hyperinflation months fall inside the third sub-period + `infl[44:63]`. +``` + +```{solution-start} fr_ex1 +:class: dropdown +``` + +```{code-cell} ipython3 +import pandas as pd + +dates = pd.date_range(start='1791-01', periods=63, freq='ME') +threshold = np.log(1.5) + +fig, ax = plt.subplots(figsize=(10, 4)) +ax.plot(dates[1:], infl[1:], color='black', lw=1, label='Monthly inflation (log change)') +ax.axhline(threshold, color='red', linestyle='--', lw=1, + label=f'50%/month threshold (log 1.5 approx {threshold:.3f})') +ax.fill_between(dates[1:], infl[1:], threshold, + where=infl[1:] > threshold, + alpha=0.35, color='red', label='Above Cagan threshold') +ax.axvline(pd.Timestamp('1793-07'), color='orange', lw=0.8, linestyle=':', + label='Sub-period boundaries') +ax.axvline(pd.Timestamp('1794-08'), color='orange', lw=0.8, linestyle=':') +ax.set_xlabel('Date') +ax.set_ylabel('Monthly inflation (log change)') +ax.set_title('Monthly inflation in Revolutionary France, 1791-1796') +ax.legend(fontsize=8) +plt.tight_layout() +plt.show() +``` + +```{code-cell} ipython3 +above = np.where(infl > threshold)[0] +print(f'Months above 50%/month threshold: {len(above)}') +for idx in above: + print(f' index {idx:3d} approx {dates[idx].strftime("%b %Y")} ' + f'(inflation = {infl[idx]:.3f})') + +# Part c +in_subperiod3 = above[above >= 44] +print(f'\nOf those, {len(in_subperiod3)} fall in sub-period 3 (index >= 44)') +``` + +The concentrated cluster of months above the threshold falls in 1795, +consistent with the lecture's "May to December 1795" statement. + +All Cagan-hyperinflation threshold months sit inside sub-period 3, confirming that the boundary the lecture uses is a good approximation to Cagan's own criterion. + +```{solution-end} +``` + +```{exercise} +:label: fr_ex2 + +**Estimating Cagan's money-demand sensitivity $\alpha$ from the data.** + +The Cagan demand-for-money function (see {doc}`cagan_ree`) implies a +*log-linear* relationship between real money balances and inflation: + +$$ +\log(M_t/p_t) = c - \alpha \pi_t +$$ + +The lecture fits a *linear* (levels) regression of `infl` on `bal` for the +hyperinflation sub-period, but Cagan's theory predicts a log-linear +relationship. + +a. Compute `log_bal = np.log(bal)`, use the `fit` helper defined in this lecture + to regress `log_bal[44:63]` on `infl[44:63]`, extract the slope $b$, and set + $\hat\alpha = -b$. + +b. Plot `infl[44:63]` on the horizontal axis and `log_bal[44:63]` on the + vertical axis, then overlay the fitted line and label it with the estimated + $\hat\alpha$. + +c. Explain why the slope $b_3$ from the lecture's levels regression of `infl` on + `bal` is less consistent with Cagan's model than the log-linear + specification in part a, and identify the distortion that arises from using + `bal` in levels during a hyperinflation. + +d. Compare $\hat\alpha$ from part a to the default value $\alpha = 5$ used in + the quantecon lecture {doc}`cagan_ree` and assess whether the order of + magnitude is consistent. +``` + +```{solution-start} fr_ex2 +:class: dropdown +``` + +```{code-cell} ipython3 +log_bal = np.log(bal) + +# Fit log_bal = a + b * infl. +a_log, b_log = fit(infl[44:63], log_bal[44:63]) +α_hat = -b_log +print(f'Log-linear estimate: alpha_hat = {α_hat:.2f}') +print(f'Default in cagan_ree: α = 5.00') +``` + +```{code-cell} ipython3 +infl_grid = np.linspace(infl[44:63].min(), infl[44:63].max(), 100) + +fig, ax = plt.subplots() +ax.scatter(infl[44:63], log_bal[44:63], + marker='*', color='orange', s=60, label='data (hyperinflation)') +ax.plot(infl_grid, a_log + b_log * infl_grid, + color='orange', lw=2, + label=fr'log-linear fit: $\hat{{\alpha}} = {α_hat:.1f}$') +ax.set_xlabel('Monthly inflation rate $\\pi_t$') +ax.set_ylabel('$\\log(\\mathrm{real\\;balances})$') +ax.set_title('Cagan demand for money - hyperinflation period') +ax.legend() +plt.tight_layout() +plt.show() +``` + +**Part c:** Cagan's model postulates an exponential fall in real balances as +inflation rises, $M/p = e^{c - \alpha\pi}$. + +Over a hyperinflation real balances vary by orders of magnitude, so a linear +fit of `infl` on `bal` is a poor global approximation because it imposes a straight line on what is an exponential curve. + +Taking logs first makes the relationship linear and the regression well-specified +across the full range of the data. + +**Part d:** $\hat\alpha$ should come out in the range of 3-6, consistent with the +default $\alpha = 5$ in {doc}`cagan_ree`, confirming that the same model estimated +on 18th-century French data gives a parameter value similar to those obtained from +20th-century hyperinflations. + +```{solution-end} +``` + +```{exercise} +:label: fr_ex3 + +**A single-figure portrait of the three monetary regimes.** + +The lecture presents the three monetary regimes through five separate scatter plots. + +Here you will synthesise them into a single figure that makes the contrasting theories visually obvious. + +Produce a figure with all three scatter clouds (blue circles, red pluses, orange +stars) and one regression line per cloud, using the coefficients already computed +in the lecture: + +- **Real bills** (indices 1:31): draw the regression line + `infl = a1 + b1 * bal` (theory says growing *backed* real balances accompany + only modest inflation). + +- **Terror** (indices 31:44): because legal restrictions suspended the normal + money-demand relationship, draw the *reversed* regression + `bal = a2_rev + b2_rev * infl` as a function of `infl`, which produces a + nearly *horizontal* line in `(bal, infl)` space and shows that real balances + were held roughly fixed by force regardless of inflation. + +- **Hyperinflation** (indices 44:63): draw `infl = a3 + b3 * bal` (the classic + negative Cagan slope, since higher inflation accompanies lower real balances). + +After producing the figure, write 2-3 sentences explaining why the Terror +regression line takes a different orientation from the other two. +``` + +```{solution-start} fr_ex3 +:class: dropdown +``` + +```{code-cell} ipython3 +fig, ax = plt.subplots(figsize=(8, 6)) + +# Real bills +ax.plot(bal[1:31], infl[1:31], 'o', markerfacecolor='none', + color='blue', label='Real bills (1791-1793)') +bal_grid1 = np.linspace(bal[1:31].min(), bal[1:31].max(), 100) +ax.plot(bal_grid1, a1 + b1 * bal_grid1, color='blue', lw=2) + +# Terror +ax.plot(bal[31:44], infl[31:44], '+', color='red', ms=9, + label='Terror (1793-1794)') +infl_grid2 = np.linspace(infl[31:44].min(), infl[31:44].max(), 100) +ax.plot(a2_rev + b2_rev * infl_grid2, infl_grid2, color='red', lw=2) + +# Hyperinflation +ax.plot(bal[44:63], infl[44:63], '*', color='orange', ms=8, + label='Hyperinflation (1794-1796)') +bal_grid3 = np.linspace(bal[44:63].min(), bal[44:63].max(), 100) +ax.plot(bal_grid3, a3 + b3 * bal_grid3, color='orange', lw=2) + +ax.set_xlabel('Real balances (millions of livres)') +ax.set_ylabel('Monthly inflation rate') +ax.set_title('Three monetary regimes in Revolutionary France') +ax.legend(fontsize=9) +plt.tight_layout() +plt.show() +``` + +During the Terror the Committee of Public Safety imposed legal restrictions, including the death penalty for refusing assignats, that forced citizens to hold high real balances regardless of how fast prices were rising. + +This broke the normal money-demand relationship because real balances were determined by government decree rather than by inflation. + +Regressing `bal` on `infl` and plotting the result as a nearly horizontal line reflects this altered direction of causation. + +Inflation varied substantially during the Terror, but real balances barely moved. + +By contrast, both the real-bills and hyperinflation clouds obey a theory in which real balances are chosen by the public and hence respond to inflation. + +```{solution-end} +``` diff --git a/lectures/geom_series.md b/lectures/geom_series.md index e33ed5c47..44c08493a 100644 --- a/lectures/geom_series.md +++ b/lectures/geom_series.md @@ -955,3 +955,254 @@ plt.show() Notice here, whether government spending increases from 0.3 to 0.4 or investment increases from 0.3 to 0.4, the shifts in the graphs are identical. + +## Exercises + +```{exercise} +:label: geom_ex1 + +Numerically verify the infinite geometric series formula + +$$ +1 + c + c^2 + \cdots = \frac{1}{1-c} +$$ + +for $c = 0.9$. + +Compute the partial sums $S_T = \sum_{t=0}^{T} c^t$ for $T = 0, 1, \ldots, 80$ +and plot them alongside the theoretical limit $\frac{1}{1-c}$. + +On a second subplot, plot the absolute error $\left|S_T - \frac{1}{1-c}\right|$ +on a log scale to illustrate the rate of convergence. +``` + +```{solution-start} geom_ex1 +:class: dropdown +``` + +```{code-cell} ipython3 +c = 0.9 +T_max = 80 +T = np.arange(0, T_max + 1) + +# Partial sums: S_T = sum_{t=0}^{T} c^t +S = np.cumsum(c**T) + +# Theoretical limit +limit = 1 / (1 - c) + +fig, axes = plt.subplots(1, 2, figsize=(12, 4)) + +# Left panel: partial sums converging to the limit +axes[0].plot(T, S, label='Partial sum $S_T$') +axes[0].axhline(limit, linestyle='--', color='red', + label=f'Limit $1/(1-c) = {limit:.1f}$') +axes[0].set_xlabel('$T$') +axes[0].set_ylabel('$S_T$') +axes[0].set_title('Convergence of partial sums') +axes[0].legend() + +# Right panel: absolute error on log scale +error = np.abs(S - limit) +axes[1].semilogy(T, error) +axes[1].set_xlabel('$T$') +axes[1].set_ylabel(r'$|S_T - 1/(1-c)|$') +axes[1].set_title('Absolute error (log scale)') + +plt.tight_layout() +plt.show() +``` + +The left panel confirms that $S_T$ converges smoothly to $1/(1-c) = 10$. + +The right panel shows that the error decays geometrically, a straight line on a log scale, reflecting the fact that the remainder after $T$ terms equals $c^{T+1}/(1-c)$. + +```{solution-end} +``` + +```{exercise} +:label: geom_ex2 + +Using the fractional reserve banking model from this lecture, suppose the +initial deposit is $D_0 = 1$. + +a. For each reserve ratio $r \in \{0.05, 0.10, 0.20, 0.40\}$, plot the + **cumulative deposits** $\sum_{i=0}^{N} D_i$ as a function of the number + of banks $N$ (for $N = 0, 1, \ldots, 50$) and add a dashed horizontal line + at the theoretical limit $D_0/r$ for each. + +b. Print the theoretical money multiplier $1/r$ for each reserve ratio. +``` + +```{solution-start} geom_ex2 +:class: dropdown +``` + +```{code-cell} ipython3 +D_0 = 1 +N_max = 50 +N = np.arange(0, N_max + 1) +reserve_ratios = [0.05, 0.10, 0.20, 0.40] + +fig, ax = plt.subplots() +for r in reserve_ratios: + # D_i = (1 - r)^i * D_0 + D = D_0 * (1 - r)**N + cumulative = np.cumsum(D) + ax.plot(N, cumulative, label=f'$r = {r}$') + ax.axhline(D_0 / r, linestyle='--', alpha=0.4) + +ax.set_xlabel('Number of banks $N$') +ax.set_ylabel('Cumulative deposits') +ax.set_title('Convergence to the money multiplier $D_0/r$') +ax.legend() +plt.show() + +# Part b +print(f"{'Reserve ratio':>15} | {'Money multiplier 1/r':>20}") +print('-' * 40) +for r in reserve_ratios: + print(f"{r:>15.2f} | {1/r:>20.2f}") +``` + +A lower reserve ratio means banks lend out a larger fraction of each deposit, so the money-creation process takes longer to run down and the total deposits created are much larger. + +The dashed lines mark the theoretical limit $D_0/r$, which the cumulative series approaches from below. + +```{solution-end} +``` + +```{exercise} +:label: geom_ex3 + +The **Gordon formula** approximates the present value of an infinite lease as + +$$ +p_0 \approx \frac{x_0}{r - g} +$$ + +Using the `infinite_lease` function defined earlier, set $x_0 = 1$ and +$r = 0.05$, and let $g$ range over $[0,\, 0.045]$. + +a. Plot the exact present value and the Gordon approximation on the same + graph as functions of $g$. + +b. On a second subplot, plot the percentage approximation error + +$$ +\text{error}(\%) = \frac{|\text{Gordon} - \text{exact}|}{\text{exact}} \times 100 +$$ + +and comment on whether the percentage error varies with $g$. +``` + +```{solution-start} geom_ex3 +:class: dropdown +``` + +```{code-cell} ipython3 +r_val = 0.05 +x_0 = 1 +g_vals = np.linspace(0, 0.045, 300) + +exact = infinite_lease(g_vals, r_val, x_0) +gordon = x_0 / (r_val - g_vals) +pct_error = np.abs(gordon - exact) / exact * 100 +pct_error_formula = 100 * r_val / (1 + r_val) + +fig, axes = plt.subplots(1, 2, figsize=(12, 4)) + +axes[0].plot(g_vals, exact, label='Exact $p_0$') +axes[0].plot(g_vals, gordon, '--', label='Gordon approximation') +axes[0].set_xlabel('$g$') +axes[0].set_ylabel('$p_0$') +axes[0].set_title(f'Infinite lease present value ($r = {r_val}$)') +axes[0].legend() + +axes[1].plot(g_vals, pct_error) +axes[1].axhline(pct_error_formula, linestyle='--', color='red', + label=fr'$100r/(1+r) = {pct_error_formula:.2f}\%$') +axes[1].set_xlabel('$g$') +axes[1].set_ylabel('Percentage error (%)') +axes[1].set_title('Gordon formula approximation error') +axes[1].legend() + +plt.tight_layout() +plt.show() +``` + +For a fixed $r$, the percentage error is constant in $g$ because the exact value is $x_0(1+r)/(r-g)$ while the Gordon approximation is $x_0/(r-g)$. + +The approximation becomes accurate when $r$ is small because the exact value differs from the Gordon formula by the factor $1+r$. + +```{solution-end} +``` + +```{exercise} +:label: geom_ex4 + +The `calculate_y` function simulates the Keynesian dynamic model. + +a. For $i = 0.3$, $g = 0.3$, $y_{-1} = 0$, and $T = 60$, plot the path + of national income $y_t$ for each $b \in \{0.25,\, 0.50,\, 0.75,\, 0.90\}$ + and mark the long-run equilibrium $y^* = (i + g)/(1 - b)$ with a dashed + horizontal line for each $b$. + +b. For each value of $b$, find the first period $T^*$ at which $y_t$ + reaches 95 percent of $y^*$, plot $T^*$ against $b$, and comment on how + the speed of convergence relates to the size of the Keynesian multiplier. +``` + +```{solution-start} geom_ex4 +:class: dropdown +``` + +```{code-cell} ipython3 +i_0, g_0, y_init = 0.3, 0.3, 0 +bs = [0.25, 0.50, 0.75, 0.90] +T = 60 + +# Part a +fig, ax = plt.subplots() +for b in bs: + y = calculate_y(i_0, b, g_0, T, y_init) + y_star = (i_0 + g_0) / (1 - b) + ax.plot(np.arange(T + 1), y, label=f'$b = {b}$, $y^* = {y_star:.1f}$') + ax.axhline(y_star, linestyle='--', alpha=0.4) + +ax.set_xlabel('$t$') +ax.set_ylabel('$y_t$') +ax.set_title('National income paths for different $b$') +ax.legend() +plt.show() + +# Part b +T_long = 1000 +T_star_vals = [] +for b in bs: + y = calculate_y(i_0, b, g_0, T_long, y_init) + y_star = (i_0 + g_0) / (1 - b) + idx = np.where(y >= 0.95 * y_star)[0] + T_star_vals.append(int(idx[0]) if len(idx) > 0 else T_long) + +fig, ax = plt.subplots() +ax.bar([str(b) for b in bs], T_star_vals) +ax.set_xlabel('Marginal propensity to consume $b$') +ax.set_ylabel('Periods to reach 95% of $y^*$') +ax.set_title('Speed of convergence to long-run equilibrium') +plt.show() + +print(f"{'b':>6} | {'Multiplier 1/(1-b)':>20} | {'T* (periods)':>14}") +print('-' * 46) +for b, T_star in zip(bs, T_star_vals): + print(f"{b:>6.2f} | {1/(1-b):>20.2f} | {T_star:>14}") +``` + +As $b$ rises toward 1, the Keynesian multiplier $1/(1-b)$ grows large and convergence slows markedly. + +This reflects the fact that the geometric series $\sum_{t=0}^\infty b^t$ converges +more slowly when $b$ is close to 1 because each additional round of spending adds +a term $b^t$ that shrinks only gradually. + +```{solution-end} +``` diff --git a/lectures/inequality.md b/lectures/inequality.md index eb0430dfb..7a8475fa1 100644 --- a/lectures/inequality.md +++ b/lectures/inequality.md @@ -55,11 +55,11 @@ Rome from across the empire, greatly enriched those in power. Meanwhile, ordinary citizens were taken from their farms to fight for long periods, diminishing their wealth. -The resulting growth in inequality was a driving factor behind political turmoil that shook the foundations of the republic. +The resulting growth in inequality was a driving factor behind political turmoil that shook the foundations of the republic. Eventually, the Roman Republic gave way to a series of dictatorships, starting with [Octavian](https://en.wikipedia.org/wiki/Augustus) (Augustus) in 27 BCE. -This history tells us that inequality matters, in the sense that it can drive major world events. +This history tells us that inequality matters, in the sense that it can drive major world events. There are other reasons that inequality might matter, such as how it affects human welfare. @@ -108,7 +108,7 @@ The Lorenz curve takes a sample $w_1, \ldots, w_n$ and produces a curve $L$. We suppose that the sample has been sorted from smallest to largest. -To aid our interpretation, suppose that we are measuring wealth +To aid our interpretation, suppose that we are measuring wealth * $w_1$ is the wealth of the poorest member of the population, and * $w_n$ is the wealth of the richest member of the population. @@ -201,11 +201,11 @@ def lorenz_curve(y): ``` In the next figure, we generate $n=2000$ draws from a lognormal -distribution and treat these draws as our population. +distribution and treat these draws as our population. -The straight 45-degree line ($x=L(x)$ for all $x$) corresponds to perfect equality. +The straight 45-degree line ($x=L(x)$ for all $x$) corresponds to perfect equality. -The log-normal draws produce a less equal distribution. +The log-normal draws produce a less equal distribution. For example, if we imagine these draws as being observations of wealth across a sample of households, then the dashed lines show that the bottom 80\% of @@ -263,9 +263,9 @@ population weights supplied by the SCF.) ```{code-cell} ipython3 :tags: [hide-input] -df = df_income_wealth +df = df_income_wealth -varlist = ['n_wealth', # net wealth +varlist = ['n_wealth', # net wealth 't_income', # total income 'l_income'] # labor income @@ -282,18 +282,18 @@ for var in varlist: for year in years: # Repeat the observations according to their weights - counts = list(round(df[df['year'] == year]['weights'] )) + counts = list(round(df[df['year'] == year]['weights'] )) y = df[df['year'] == year][var].repeat(counts) y = np.asarray(y) - + # Shuffle the sequence to improve the plot - rd.shuffle(y) - + rd.shuffle(y) + # calculate and store Lorenz curve data f_val, l_val = lorenz_curve(y) f_vals.append(f_val) l_vals.append(l_val) - + F_vals.append(f_vals) L_vals.append(l_vals) @@ -328,7 +328,7 @@ ax.legend() plt.show() ``` -One key finding from this figure is that wealth inequality is more extreme than income inequality. +One key finding from this figure is that wealth inequality is more extreme than income inequality. @@ -349,7 +349,7 @@ In this section we discuss the Gini coefficient and its relationship to the Lore As before, suppose that the sample $w_1, \ldots, w_n$ has been sorted from smallest to largest. -The Gini coefficient is defined for the sample above as +The Gini coefficient is defined for the sample above as ```{prf:definition} :label: define-gini @@ -395,7 +395,7 @@ $$ G = \frac{A}{A+B} $$ -where $A$ is the area between the 45-degree line of +where $A$ is the area between the 45-degree line of perfect equality and the Lorenz curve, while $B$ is the area below the Lorenze curve -- see {numref}`lorenz_gini2`. ```{code-cell} ipython3 @@ -464,7 +464,7 @@ def gini_coefficient(y): Now we can compute the Gini coefficients for five different populations. -Each of these populations is generated by drawing from a +Each of these populations is generated by drawing from a lognormal distribution with parameters $\mu$ (mean) and $\sigma$ (standard deviation). To create the five populations, we vary $\sigma$ over a grid of length $5$ @@ -472,7 +472,7 @@ between $0.2$ and $4$. In each case we set $\mu = - \sigma^2 / 2$. -This implies that the mean of the distribution does not change with $\sigma$. +This implies that the mean of the distribution does not change with $\sigma$. You can check this by looking up the expression for the mean of a lognormal distribution. @@ -512,10 +512,10 @@ mystnb: caption: Gini coefficients of simulated data name: gini_simulated --- -fix, ax = plot_inequality_measures(σ_vals, - ginis, - 'simulated', - r'$\sigma$', +fix, ax = plot_inequality_measures(σ_vals, + ginis, + 'simulated', + r'$\sigma$', 'Gini coefficients') plt.show() ``` @@ -551,7 +551,7 @@ mystnb: # Fetch gini data for all countries gini_all = wb.data.DataFrame("SI.POV.GINI") # remove 'YR' in index and convert to integer -gini_all.columns = gini_all.columns.map(lambda x: int(x.replace('YR',''))) +gini_all.columns = gini_all.columns.map(lambda x: int(x.replace('YR',''))) # Create a long series with a multi-index of the data to get global min and max values gini_all = gini_all.unstack(level='economy').dropna() @@ -658,7 +658,7 @@ data = gini_all.unstack() data.columns ``` -There are 167 countries represented in this dataset. +There are 167 countries represented in this dataset. Let us compare three advanced economies: the US, the UK, and Norway @@ -684,7 +684,7 @@ Let us take a closer look at the underlying data and see if we can rectify this. data[['NOR']].dropna().head(n=5) ``` -The data for Norway in this dataset goes back to 1979 but there are gaps in the time series and matplotlib is not showing those data points. +The data for Norway in this dataset goes back to 1979 but there are gaps in the time series and matplotlib is not showing those data points. We can use the `.ffill()` method to copy and bring forward the last known value in a series to fill in these gaps @@ -704,7 +704,7 @@ plt.show() ``` From this plot we can observe that the US has a higher Gini coefficient (i.e. -higher income inequality) when compared to the UK and Norway. +higher income inequality) when compared to the UK and Norway. Norway has the lowest Gini coefficient over the three economies and, moreover, the Gini coefficient shows no upward trend. @@ -713,7 +713,7 @@ the Gini coefficient shows no upward trend. ### Gini Coefficient and GDP per capita (over time) -We can also look at how the Gini coefficient compares with GDP per capita (over time). +We can also look at how the Gini coefficient compares with GDP per capita (over time). Let's take another look at the US, Norway, and the UK. @@ -721,7 +721,7 @@ Let's take another look at the US, Norway, and the UK. countries = ['USA', 'NOR', 'GBR'] gdppc = wb.data.DataFrame("NY.GDP.PCAP.KD", countries) # remove 'YR' in index and convert to integer -gdppc.columns = gdppc.columns.map(lambda x: int(x.replace('YR',''))) +gdppc.columns = gdppc.columns.map(lambda x: int(x.replace('YR',''))) gdppc = gdppc.T ``` @@ -750,7 +750,7 @@ min_year = plot_data.year.min() max_year = plot_data.year.max() ``` -The time series for all three countries start and stop in different years. +The time series for all three countries start and stop in different years. We will add a year mask to the data to improve clarity in the chart including the different end years associated with each country's time series. @@ -763,11 +763,11 @@ plot_data.year = plot_data.year.map(lambda x: x if x in labels else None) (fig:plotly-gini-gdppc-years)= ```{code-cell} ipython3 -fig = px.line(plot_data, - x = "gini", - y = "gdppc", - color = "country", - text = "year", +fig = px.line(plot_data, + x = "gini", + y = "gdppc", + color = "country", + text = "year", height = 800, labels = {"gini" : "Gini coefficient", "gdppc" : "GDP per capita"} ) @@ -780,13 +780,13 @@ This figure is built using `plotly` and is {ref}` available on the website a', 'c', + item_to_remove = ['a', 'c', 'd', 'e'] # b represents a billion @@ -257,29 +257,29 @@ def process_entry(entry): def process_df(df): "Clean and reorganize the entire dataframe." - + # Remove HTML markers from column names for item in ['a', 'c', 'd', 'e']: df.columns = df.columns.str.replace(item, '') - + # Convert years to int df['Year'] = df['Year'].apply(lambda x: int(x)) - + # Set index to datetime with year and month df = df.set_index( pd.to_datetime( (df['Year'].astype(str) + \ - df['Month'].astype(str)), + df['Month'].astype(str)), format='%Y%B')) df = df.drop(['Year', 'Month'], axis=1) - + # Handle duplicates by keeping the first df = df[~df.index.duplicated(keep='first')] - + # Convert attribute values to numeric df = df.map(lambda x: float(x) \ - if x != '—' else np.nan) - + if x != '\u2014' else np.nan) + # Finally, we only focus on data between 1919 and 1925 mask = (df.index >= '1919-01-01') & \ (df.index < '1925-01-01') @@ -288,7 +288,7 @@ def process_df(df): return df ``` -Now we write plotting functions `pe_plot` and `pr_plot` that will build figures that show the price level, exchange rates, +Now we write plotting functions `pe_plot` and `pr_plot` that will build figures that show the price level, exchange rates, and inflation rates, for each country of interest. ```{code-cell} ipython3 @@ -298,19 +298,19 @@ def pe_plot(p_seq, e_seq, index, labs, ax): "Generate plots for price and exchange rates." p_lab, e_lab = labs - + # Plot price and exchange rates ax.plot(index, p_seq, label=p_lab, color='tab:blue', lw=2) - + # Add a new axis ax1 = ax.twinx() ax1.plot([None], [None], label=p_lab, color='tab:blue', lw=2) ax1.plot(index, e_seq, label=e_lab, color='tab:orange', lw=2) - + # Set log axes ax.set_yscale('log') ax1.set_yscale('log') - + # Define the axis label format ax.xaxis.set_major_locator( mdates.MonthLocator(interval=5)) @@ -318,13 +318,13 @@ def pe_plot(p_seq, e_seq, index, labs, ax): mdates.DateFormatter('%b %Y')) for label in ax.get_xticklabels(): label.set_rotation(45) - + # Set labels ax.set_ylabel('Price level') ax1.set_ylabel('Exchange rate') - + ax1.legend(loc='upper left') - + return ax1 def pr_plot(p_seq, index, ax): @@ -332,22 +332,22 @@ def pr_plot(p_seq, index, ax): # Calculate the difference of log p_seq log_diff_p = np.diff(np.log(p_seq)) - + # Calculate and plot moving average diff_smooth = pd.DataFrame(log_diff_p).rolling(3, center=True).mean() ax.plot(index[1:], diff_smooth, label='Moving average (3 period)', alpha=0.5, lw=2) ax.set_ylabel('Inflation rate') - + ax.xaxis.set_major_locator( mdates.MonthLocator(interval=5)) ax.xaxis.set_major_formatter( mdates.DateFormatter('%b %Y')) - + for label in ax.get_xticklabels(): label.set_rotation(45) - + ax.legend() - + return ax ``` @@ -359,30 +359,30 @@ data_url = "https://github.com/QuantEcon/lecture-python-intro/raw/main/lectures/ xls = pd.ExcelFile(data_url) # Select relevant sheets -sheet_index = [(2, 3, 4), - (9, 10), - (14, 15, 16), +sheet_index = [(2, 3, 4), + (9, 10), + (14, 15, 16), (21, 18, 19)] # Remove redundant rows -remove_row = [(-2, -2, -2), - (-7, -10), - (-6, -4, -3), +remove_row = [(-2, -2, -2), + (-7, -10), + (-6, -4, -3), (-19, -3, -6)] # Unpack and combine series for each country df_list = [] for i in range(4): - + indices, rows = sheet_index[i], remove_row[i] - + # Apply process_entry on the selected sheet sheet_list = [ - pd.read_excel(xls, 'Table3.' + str(ind), + pd.read_excel(xls, 'Table3.' + str(ind), header=1).iloc[:row].map(process_entry) for ind, row in zip(indices, rows)] - + sheet_list = [process_df(df) for df in sheet_list] df_list.append(pd.concat(sheet_list, axis=1)) @@ -393,12 +393,12 @@ Now let's construct graphs for our four countries. For each country, we'll plot two graphs. -The first graph plots logarithms of +The first graph plots logarithms of * price levels * exchange rates vis-à-vis US dollars -For each country, the scale on the right side of a graph will pertain to the price level while the scale on the left side of a graph will pertain to the exchange rate. +For each country, the scale on the right side of a graph will pertain to the price level while the scale on the left side of a graph will pertain to the exchange rate. For each country, the second graph plots a centered three-month moving average of the inflation rate defined as $\frac{p_{t-1} + p_t + p_{t+1}}{3}$. @@ -419,7 +419,7 @@ mystnb: p_seq = df_aus['Retail price index, 52 commodities'] e_seq = df_aus['Exchange Rate'] -lab = ['Retail price index', +lab = ['Retail price index', 'Austrian Krones (Crowns) per US cent'] # Create plot @@ -447,8 +447,8 @@ Staring at {numref}`pi_xrate_austria` and {numref}`inflationrate_austria` convey * an episode of "hyperinflation" with rapidly rising log price level and very high monthly inflation rates * a sudden stop of the hyperinflation as indicated by the abrupt flattening of the log price level and a marked permanent drop in the three-month average of inflation -* a US dollar exchange rate that shadows the price level. - +* a US dollar exchange rate that shadows the price level. + We'll see similar patterns in the next three episodes that we'll study now. ### Hungary @@ -467,7 +467,7 @@ mystnb: p_seq = df_hun['Hungarian index of prices'] e_seq = 1 / df_hun['Cents per crown in New York'] -lab = ['Hungarian index of prices', +lab = ['Hungarian index of prices', 'Hungarian Koronas (Crowns) per US cent'] # Create plot @@ -495,7 +495,7 @@ plt.show() The sources of our data for Poland are: -* Table 3.15, price level $\exp p$ +* Table 3.15, price level $\exp p$ * Table 3.15, exchange rate ```{note} @@ -514,7 +514,7 @@ mystnb: p_seq1 = df_pol['Wholesale price index'].copy() p_seq2 = df_pol['Wholesale Price Index: ' 'On paper currency basis'].copy() -p_seq3 = df_pol['Wholesale Price Index: ' +p_seq3 = df_pol['Wholesale Price Index: ' 'On zloty basis'].copy() # Non-nan part @@ -525,8 +525,8 @@ adj_ratio12 = (p_seq1[mask_1] / p_seq2[mask_1]) adj_ratio23 = (p_seq2[mask_2] / p_seq3[mask_2]) # Glue three series -p_seq = pd.concat([p_seq1[:mask_1], - adj_ratio12 * p_seq2[mask_1:mask_2], +p_seq = pd.concat([p_seq1[:mask_1], + adj_ratio12 * p_seq2[mask_1:mask_2], adj_ratio23 * p_seq3[mask_2:]]) p_seq = p_seq[~p_seq.index.duplicated(keep='first')] @@ -536,7 +536,7 @@ e_seq[e_seq.index > '05-01-1924'] = np.nan ``` ```{code-cell} ipython3 -lab = ['Wholesale price index', +lab = ['Wholesale price index', 'Polish marks per US cent'] # Create plot @@ -564,7 +564,7 @@ plt.show() The sources of our data for Germany are the following tables from chapter 3 of {cite}`sargent2013rational`: -* Table 3.18, wholesale price level $\exp p$ +* Table 3.18, wholesale price level $\exp p$ * Table 3.19, exchange rate ```{code-cell} ipython3 @@ -578,7 +578,7 @@ p_seq = df_deu['Price index (on basis of marks before July 1924,' ' reichsmarks after)'].copy() e_seq = 1/df_deu['Cents per mark'] -lab = ['Price index', +lab = ['Price index', 'Marks per US cent'] # Create plot @@ -600,12 +600,12 @@ p_seq = df_deu['Price index (on basis of marks before July 1924,' e_seq = 1/df_deu['Cents per mark'].copy() # Adjust the price level/exchange rate after the currency reform -p_seq[p_seq.index > '06-01-1924'] = p_seq[p_seq.index +p_seq[p_seq.index > '06-01-1924'] = p_seq[p_seq.index > '06-01-1924'] * 1e12 -e_seq[e_seq.index > '12-01-1923'] = e_seq[e_seq.index +e_seq[e_seq.index > '12-01-1923'] = e_seq[e_seq.index > '12-01-1923'] * 1e12 -lab = ['Price index (marks or converted to marks)', +lab = ['Price index (marks or converted to marks)', 'Marks per US cent(or reichsmark converted to mark)'] # Create plot @@ -635,10 +635,10 @@ It is striking how *quickly* (log) price levels in Austria, Hungary, Poland, and These "sudden stops" are also revealed by the permanent drops in three-month moving averages of inflation for the four countries plotted above. -In addition, the US dollar exchange rates for each of the four countries shadowed their price levels. +In addition, the US dollar exchange rates for each of the four countries shadowed their price levels. ```{note} -This pattern is an instance of a force featured in the [purchasing power parity](https://en.wikipedia.org/wiki/Purchasing_power_parity) theory of exchange rates. +This pattern is an instance of a force featured in the [purchasing power parity](https://en.wikipedia.org/wiki/Purchasing_power_parity) theory of exchange rates. ``` Each of these big inflations seemed to have "stopped on a dime". @@ -647,22 +647,22 @@ Chapter 3 of {cite}`sargent2002big` offers an explanation for this remarkable pa In a nutshell, here is the explanation offered there. -After World War I, the United States was on a gold standard. +After World War I, the United States was on a gold standard. The US government stood ready to convert a dollar into a specified amount of gold on demand. -Immediately after World War I, Hungary, Austria, Poland, and Germany were not on the gold standard. +Immediately after World War I, Hungary, Austria, Poland, and Germany were not on the gold standard. Their currencies were "fiat" or "unbacked", meaning that they were not backed by credible government promises to convert them into gold or silver coins on demand. -The governments printed new paper notes to pay for goods and services. +The governments printed new paper notes to pay for goods and services. ```{note} Technically the notes were "backed" mainly by treasury bills. But people could not expect that those treasury bills would be paid off by levying taxes, but instead by printing more notes or treasury bills. ``` -This was done on such a scale that it led to a depreciation of the currencies of spectacular proportions. - +This was done on such a scale that it led to a depreciation of the currencies of spectacular proportions. + In the end, the German mark stabilized at 1 trillion ($10^{12}$) paper marks to the prewar gold mark, the Polish mark at 1.8 million paper marks to the gold zloty, the Austrian crown at 14,400 paper crowns to the prewar Austro-Hungarian crown, and the Hungarian krone at 14,500 paper crowns to the prewar Austro-Hungarian crown. Chapter 3 of {cite}`sargent2002big` described deliberate changes in policy that Hungary, Austria, Poland, and Germany made to end their hyperinflations. @@ -672,3 +672,266 @@ Each government stopped printing money to pay for goods and services once again The story told in {cite}`sargent2002big` is grounded in a *monetarist theory of the price level* described in {doc}`cagan_ree` and {doc}`cagan_adaptive`. Those lectures discuss theories about what owners of those rapidly depreciating currencies were thinking and how their beliefs shaped responses of inflation to government monetary and fiscal policies. + +## Exercises + +```{exercise} +:label: ih_ex1 + +**Comparing peak monthly inflation rates across the four hyperinflations.** + +For each of the four post-World War I hyperinflationary episodes (Austria, +Hungary, Poland, Germany), compute the peak monthly log-inflation rate +$\Delta \log p_t = \log p_t - \log p_{t-1}$ and the calendar month in which +it occurred. + +a. Display the four peak log-changes in a bar chart. + +b. Convert each peak log-change to a monthly percentage price rise + (i.e., compute $100 \times (e^{\Delta \log p_t} - 1)$) and print a + short table of peak rates and dates. + +c. Which country experienced the most extreme peak monthly inflation? +``` + +```{solution-start} ih_ex1 +:class: dropdown +``` + +```{code-cell} ipython3 +# Price series directly available from the lecture +p_aus = df_aus['Retail price index, 52 commodities'].dropna() +p_hun = df_hun['Hungarian index of prices'].dropna() +p_deu = df_deu['Price index (on basis of marks before July 1924,' + ' reichsmarks after)'].dropna() + +# Reconstruct the spliced Poland series (following the lecture body) +p_s1 = df_pol['Wholesale price index'].copy() +p_s2 = df_pol['Wholesale Price Index: On paper currency basis'].copy() +p_s3 = df_pol['Wholesale Price Index: On zloty basis'].copy() +m1 = p_s1[~p_s1.isna()].index[-1] +m2 = p_s2[~p_s2.isna()].index[-2] +r12 = p_s1[m1] / p_s2[m1] +r23 = p_s2[m2] / p_s3[m2] +p_pol = pd.concat([p_s1[:m1], + r12 * p_s2[m1:m2], + r23 * p_s3[m2:]]).dropna() + +p_series = {'Austria': p_aus, 'Hungary': p_hun, + 'Poland': p_pol, 'Germany': p_deu} + +# Compute peak monthly log-inflation and its date for each country +peak_log = {} +peak_date = {} +for country, p in p_series.items(): + log_infl = pd.Series(np.diff(np.log(p.values)), index=p.index[1:]) + peak_log[country] = log_infl.max() + peak_date[country] = log_infl.idxmax() + +# Part b: print table +print(f"{'Country':<10} {'Peak log-change':>16} {'Monthly % rise':>14} {'Date'}") +print('-' * 62) +for c in p_series: + pct = 100 * (np.exp(peak_log[c]) - 1) + print(f"{c:<10} {peak_log[c]:>16.3f} {pct:>13.1f}% " + f"{peak_date[c].strftime('%b %Y')}") + +# Part a: bar chart +fig, ax = plt.subplots() +countries = list(p_series.keys()) +ax.bar(countries, [peak_log[c] for c in countries], color='steelblue') +ax.set_ylabel('Peak monthly log-inflation rate') +ax.set_title('Peak monthly inflation during the four post-WWI hyperinflations') +plt.tight_layout() +plt.show() +``` + +The table and bar chart show that Germany's hyperinflation dwarfed the others because its peak monthly log-inflation rate, reached in October 1923, translates into a monthly price increase of about 296-fold. + +Austria, Hungary, and Poland experienced severe inflations by any historical +standard, but they were modest in comparison. + +```{solution-end} +``` + +```{exercise} +:label: ih_ex2 + +**Gold standard versus fiat money: quantifying long-run price stability.** + +The lecture argues that abandoning the gold/silver standard after 1914 unleashed +persistent inflation that had been absent over the preceding three centuries. + +Using the `df_fig5` dataframe, test this claim quantitatively. For each +country in `df_fig5` (UK, US, France, Castile), compute the annualized +average log-growth rate of the price level for: + +* the gold-standard era: years 1700 to 1913, and +* the fiat-money era: years 1914 to 2000. + +The annualized rate for a country whose price level rises from $p_{t_1}$ in +year $t_1$ to $p_{t_2}$ in year $t_2$ is + +$$ +g = \frac{\log p_{t_2} - \log p_{t_1}}{t_2 - t_1}. +$$ + +Display your results in a grouped bar chart and comment on what you find. +``` + +```{solution-start} ih_ex2 +:class: dropdown +``` + +```{code-cell} ipython3 +periods = { + '1700-1913\n(gold standard)': (1700, 1913), + '1914-2000\n(fiat money)': (1914, 2000), +} +cols_fig5 = ['UK', 'US', 'France', 'Castile'] +rates = {col: {} for col in cols_fig5} + +for col in cols_fig5: + series = df_fig5[col].dropna() + for label, (y1, y2) in periods.items(): + sub = series[(series.index >= y1) & (series.index <= y2)] + if len(sub) >= 2: + rates[col][label] = ( + (np.log(float(sub.iloc[-1])) - np.log(float(sub.iloc[0]))) + / (sub.index[-1] - sub.index[0]) + ) + else: + rates[col][label] = np.nan + +x = np.arange(len(cols_fig5)) +width = 0.35 +era_labels = list(periods.keys()) + +fig, ax = plt.subplots(figsize=(9, 5)) +for i, label in enumerate(era_labels): + vals = [rates[c].get(label, np.nan) for c in cols_fig5] + ax.bar(x + (i - 0.5) * width, vals, width, label=label) + +ax.set_xticks(x) +ax.set_xticklabels(cols_fig5) +ax.set_ylabel('Annualized log-price growth rate') +ax.set_title('Price-level growth: gold standard era vs. fiat money era') +ax.axhline(0, color='black', lw=0.8) +ax.legend() +plt.tight_layout() +plt.show() +``` + +The chart confirms the lecture's central message. + +During the three centuries of the gold and silver standard, 1700 to 1913, the +annualized log-price growth rate was close to zero for all four countries. + +After 1914, when governments left the gold standard and gained the ability to +print money, average annual inflation jumped markedly for every country with sufficient post-1914 data. + +The Castile series does not extend reliably into the 20th century, so its +fiat-era bar reflects incomplete data and should be interpreted with caution. + +```{solution-end} +``` + +```{exercise} +:label: ih_ex3 + +**Purchasing power parity during the German hyperinflation.** + +The lecture states that the US dollar exchange rate for each country +"shadowed" its price level. + +This co-movement is a hallmark of +*purchasing power parity* (PPP), which predicts that $\log e_t \approx +\log p_t + \text{const}$, so the *real exchange rate* +$q_t = \log e_t - \log p_t$ should be approximately constant. + +Examine PPP for the German episode: + +a. Normalize both the log price level and the log exchange rate (marks per + US cent) to zero at the first available date, plot both normalized + series on the same axes, and assess how closely they track each other. + +b. Compute the Pearson correlation between the two normalized log-level + series and print it. + +c. Plot the real exchange rate $q_t = \log e_t - \log p_t$ over time and + compare the standard deviation of $q_t$ with the standard deviation of + $\log p_t$ to assess how large the deviations from PPP are relative to + the overall price movement. +``` + +```{solution-start} ih_ex3 +:class: dropdown +``` + +```{code-cell} ipython3 +# Extract Germany price and exchange-rate series and align on common dates +p_ger = df_deu['Price index (on basis of marks before July 1924,' + ' reichsmarks after)'].dropna() +e_ger = (1 / df_deu['Cents per mark']).dropna() + +# Convert post-reform reichsmark observations back into paper-mark units +p_ger[p_ger.index > '1924-06-01'] = p_ger[p_ger.index > '1924-06-01'] * 1e12 +e_ger[e_ger.index > '1923-12-01'] = e_ger[e_ger.index > '1923-12-01'] * 1e12 + +common = p_ger.index.intersection(e_ger.index) +log_p = np.log(p_ger[common]) +log_e = np.log(e_ger[common]) + +# Normalize to zero at the first common date +log_p_n = log_p - log_p.iloc[0] +log_e_n = log_e - log_e.iloc[0] + +# Part a: plot normalized log levels +fig, ax = plt.subplots(figsize=(9, 4)) +ax.plot(common, log_p_n, label='Log price level (normalized)', lw=2) +ax.plot(common, log_e_n, label='Log exchange rate (normalized)', + lw=2, linestyle='--') +ax.set_ylabel('Log level (normalized to 0 at start)') +ax.set_title('PPP check: Germany 1919-1924') +ax.legend() +ax.xaxis.set_major_locator(mdates.MonthLocator(interval=5)) +ax.xaxis.set_major_formatter(mdates.DateFormatter('%b %Y')) +for lbl in ax.get_xticklabels(): + lbl.set_rotation(45) +plt.tight_layout() +plt.show() + +# Part b: Pearson correlation +corr = np.corrcoef(log_p_n.values, log_e_n.values)[0, 1] +print(f"Pearson correlation between log price and log exchange rate: {corr:.4f}") + +# Part c: real exchange rate q_t = log e - log p +q = log_e - log_p +fig, ax = plt.subplots(figsize=(9, 3)) +ax.plot(common, q, lw=2, color='tab:green') +ax.set_ylabel(r'$q_t = \log e_t - \log p_t$') +ax.set_title('Real exchange rate: Germany 1919-1924') +ax.xaxis.set_major_locator(mdates.MonthLocator(interval=5)) +ax.xaxis.set_major_formatter(mdates.DateFormatter('%b %Y')) +for lbl in ax.get_xticklabels(): + lbl.set_rotation(45) +plt.tight_layout() +plt.show() + +print(f"Std dev of log price level (normalized): {log_p_n.std():.3f}") +print(f"Std dev of real exchange rate q: {q.std():.3f}") +``` + +For parts a and b, the two normalized log series are nearly indistinguishable and their Pearson correlation is very close to 1. + +During Germany's hyperinflation, every tenfold rise in the domestic price level +was matched by an approximately tenfold rise in the exchange rate, as PPP predicts. + +For part c, the real exchange rate $q_t$ fluctuates only modestly compared with the enormous swings in $\log p_t$. + +Its standard deviation is a small fraction of the standard deviation of the normalized +log price level, confirming that exchange rate movements were driven almost entirely +by domestic price inflation with only minor transient deviations from PPP. + +```{solution-end} +``` diff --git a/lectures/input_output.md b/lectures/input_output.md index 6c768cea1..04959b5e7 100644 --- a/lectures/input_output.md +++ b/lectures/input_output.md @@ -41,7 +41,7 @@ mpl.rcParams.update(mpl.rcParamsDefault) ``` The following figure illustrates a network of linkages among 15 sectors -obtained from the US Bureau of Economic Analysis’s 2021 Input-Output Accounts +obtained from the US Bureau of Economic Analysis's 2021 Input-Output Accounts Data. @@ -200,7 +200,6 @@ This can be graphically represented as follows. :tags: [hide-input] fig, ax = plt.subplots() -ax.grid() # Draw constraint lines ax.hlines(0, -1, 400) @@ -457,7 +456,6 @@ The dual problem can be graphically represented as follows. :tags: [hide-input] fig, ax = plt.subplots() -ax.grid() # Draw constraint lines ax.hlines(0, -1, 50) @@ -562,13 +560,13 @@ Another way to rank sectors in input-output networks is via output multipliers. The **output multiplier** of sector $j$ denoted by $\mu_j$ is usually defined as the total sector-wide impact of a unit change of demand in sector $j$. -Earlier when disussing demand shocks we concluded that for $L = (l_{ij})$ the element +Earlier when discussing demand shocks we concluded that for $L = (l_{ij})$ the element $l_{ij}$ represents the impact on sector $i$ of a unit change in demand in sector $j$. Thus, $$ -\mu_j = \sum_{j=1}^n l_{ij} +\mu_j = \sum_{i=1}^n l_{ij} $$ This can be written as $\mu^\top = \mathbb{1}^\top L$ or diff --git a/lectures/intro_supply_demand.md b/lectures/intro_supply_demand.md index 575a4ccc1..446cb8bac 100644 --- a/lectures/intro_supply_demand.md +++ b/lectures/intro_supply_demand.md @@ -62,7 +62,7 @@ from collections import namedtuple ## Consumer surplus -Before we look at the model of supply and demand, it will be helpful to have some background on (a) consumer and producer surpluses and (b) integration. +Before we look at the model of supply and demand, it will be helpful to have some background on consumer and producer surpluses and on integration. (If you are comfortable with both topics you can jump to the {ref}`next section `.) @@ -789,8 +789,7 @@ $$ Here $p$ is set to $i_s(q)$. -Social welfare is the sum of consumer and producer surplus under the -assumption that the price is the same for buyers and sellers: +Planner welfare is the area between inverse demand and inverse supply: $$ W(q) @@ -801,6 +800,8 @@ Solve the integrals and write a function to compute this quantity numerically at given $q$. Plot welfare as a function of $q$. + +At the competitive equilibrium, the common price lets this same area be interpreted as consumer surplus plus producer surplus. ``` @@ -883,8 +884,7 @@ print(f"{maximizing_q: .5f}") ````{exercise} :label: isd_ex4 -Now compute the equilibrium quantity by finding the price that equates supply -and demand. +Now compute the equilibrium quantity by finding the quantity that equates supply and demand. You can do this numerically by finding the root of the excess demand function @@ -904,8 +904,7 @@ Initialize `newton` with a starting guess somewhere close to 1.0. (Similar initial conditions will give the same result.) -You should find that the equilibrium price agrees with the welfare maximizing -price, in line with the first fundamental welfare theorem. +You should find that the equilibrium quantity agrees with the welfare maximizing quantity, in line with the first fundamental welfare theorem. ```` diff --git a/lectures/laffer_adaptive.md b/lectures/laffer_adaptive.md index b91240d90..69bed3ee1 100644 --- a/lectures/laffer_adaptive.md +++ b/lectures/laffer_adaptive.md @@ -11,7 +11,7 @@ kernelspec: name: python3 --- -# Laffer Curves with Adaptive Expectations +# Laffer Curves with Adaptive Expectations ## Overview @@ -22,21 +22,21 @@ As in the lecture {doc}`money_inflation`, this lecture uses the log-linear versi But now, instead of assuming ''rational expectations'' in the form of ''perfect foresight'', we'll adopt the ''adaptive expectations'' assumption used by {cite}`Cagan` and {cite}`Friedman1956`. -This means that instead of assuming that expected inflation $\pi_t^*$ is described by the "perfect foresight" or "rational expectations" hypothesis +This means that instead of assuming that expected inflation $\pi_t^*$ is described by the "perfect foresight" or "rational expectations" hypothesis $$ \pi_t^* = p_{t+1} - p_t -$$ +$$ -that we adopted in lectures {doc}`money_inflation` and lectures {doc}`money_inflation_nonlinear`, we'll now assume that $\pi_t^*$ is determined by the adaptive expectations hypothesis described in equation {eq}`eq:adaptex` reported below. +that we adopted in lectures {doc}`money_inflation` and lectures {doc}`money_inflation_nonlinear`, we'll now assume that $\pi_t^*$ is determined by the adaptive expectations hypothesis described in equation {eq}`eq:adaptex` reported below. We shall discover that changing our hypothesis about expectations formation in this way will change some our findings and leave others intact. In particular, we shall discover that -* replacing rational expectations with adaptive expectations leaves the two stationary inflation rates unchanged, but that $\ldots$ +* replacing rational expectations with adaptive expectations leaves the two stationary inflation rates unchanged, but that $\ldots$ * it reverses the perverse dynamics by making the **lower** stationary inflation rate the one to which the system typically converges * a more plausible comparative dynamic outcome emerges in which now inflation can be **reduced** by running **lower** government deficits -These more plausible comparative dynamics underlie the "old time religion" that states that +These more plausible comparative dynamics underlie the "old time religion" that states that "inflation is always and everywhere caused by government deficits". These issues were studied by {cite}`bruno1990seigniorage`. @@ -46,22 +46,23 @@ predictions of their model under rational expectations (i.e., perfect foresight by dropping rational expectations and instead assuming that people form expectations about future inflation rates according to the "adaptive expectations" scheme {eq}`eq:adaptex` described below. ```{note} -{cite}`sargent1989least` had studied another way of selecting stationary equilibrium that involved replacing rational expectations with a model of learning via least squares regression. +{cite}`sargent1989least` had studied another way of selecting stationary equilibrium that involved replacing rational expectations with a model of learning via least squares regression. + {cite}`marcet2003recurrent` and {cite}`sargent2009conquest` extended that work and applied it to study recurrent high-inflation episodes in Latin America. -``` +``` ## The model -Let +Let * $m_t$ be the log of the money supply at the beginning of time $t$ * $p_t$ be the log of the price level at time $t$ -* $\pi_t^*$ be the public's expectation of the rate of inflation between $t$ and $t+1$ - +* $\pi_t^*$ be the public's expectation of the rate of inflation between $t$ and $t+1$ + The law of motion of the money supply is -$$ -\exp(m_{t+1}) - \exp(m_t) = g \exp(p_t) +$$ +\exp(m_{t+1}) - \exp(m_t) = g \exp(p_t) $$ (eq:ada_msupply) where $g$ is the part of government expenditures financed by printing money. @@ -72,15 +73,15 @@ $$ m_{t+1} = \log[ \exp(m_t) + g \exp(p_t)] $$ (eq:ada_msupply2) -The demand function for money is +The demand function for money is $$ -m_{t+1} - p_t = -\alpha \pi_t^* +m_{t+1} - p_t = -\alpha \pi_t^* $$ (eq:ada_mdemand) -where $\alpha \geq 0$. +where $\alpha \geq 0$. -Expectations of inflation are governed by +Expectations of inflation are governed by $$ \pi_{t}^* = (1-\delta) (p_t - p_{t-1}) + \delta \pi_{t-1}^* @@ -88,7 +89,7 @@ $$ (eq:adaptex) where $\delta \in (0,1)$ -## Computing an equilibrium sequence +## Computing an equilibrium sequence Equation the expressions for $m_{t+1}$ provided by {eq}`eq:ada_mdemand` and {eq}`eq:ada_msupply2` and use equation {eq}`eq:adaptex` to eliminate $\pi_t^*$ to obtain the following equation for $p_t$: @@ -105,16 +106,16 @@ Starting at time $0$ with initial conditions $(m_0, \pi_{-1}^*, p_{-1})$, for ea deploy the following steps in order: * solve {eq}`eq:pequation` for $p_t$ -* solve equation {eq}`eq:adaptex` for $\pi_t^*$ +* solve equation {eq}`eq:adaptex` for $\pi_t^*$ * solve equation {eq}`eq:ada_msupply2` for $m_{t+1}$ This completes the algorithm. ## Claims or conjectures - - -It will turn out that + + +It will turn out that * if they exist, limiting values $\overline \pi$ and $\overline \mu$ will be equal @@ -125,14 +126,14 @@ It will turn out that * for each of the two possible limiting values $\bar \pi$ ,there is a unique initial log price level $p_0$ that implies that $\pi_t = \mu_t = \bar \mu$ for all $t \geq 0$ * this unique initial log price level solves $\log(\exp(m_0) + g \exp(p_0)) - p_0 = - \alpha \bar \pi $ - + * the preceding equation for $p_0$ comes from $m_1 - p_0 = - \alpha \bar \pi$ ## Limiting values of inflation rate As in our earlier lecture {doc}`money_inflation_nonlinear`, we can compute the two prospective limiting values for $\bar \pi$ by studying the steady-state Laffer curve. -Thus, in a **steady state** +Thus, in a **steady state** $$ m_{t+1} - m_t = p_{t+1} - p_t = x \quad \forall t , @@ -143,20 +144,20 @@ where $x > 0 $ is a common rate of growth of logarithms of the money supply and A few lines of algebra yields the following equation that $x$ satisfies $$ -\exp(-\alpha x) - \exp(-(1 + \alpha) x) = g +\exp(-\alpha x) - \exp(-(1 + \alpha) x) = g $$ (eq:ada_steadypi) where we require that $$ -g \leq \max_{x: x \geq 0} \exp(-\alpha x) - \exp(-(1 + \alpha) x) , +g \leq \max_{x: x \geq 0} \exp(-\alpha x) - \exp(-(1 + \alpha) x) , $$ (eq:ada_revmax) so that it is feasible to finance $g$ by printing money. The left side of {eq}`eq:ada_steadypi` is steady state revenue raised by printing money. -The right side of {eq}`eq:ada_steadypi` is the quantity of time $t$ goods that the government raises by printing money. +The right side of {eq}`eq:ada_steadypi` is the quantity of time $t$ goods that the government raises by printing money. Soon we'll plot the left and right sides of equation {eq}`eq:ada_steadypi`. @@ -181,7 +182,7 @@ from scipy.optimize import root, fsolve Let's create a `namedtuple` to store the parameters of the model ```{code-cell} ipython3 -LafferAdaptive = namedtuple('LafferAdaptive', +LafferAdaptive = namedtuple('LafferAdaptive', ["m0", # log of the money supply at t=0 "α", # sensitivity of money demand "g", # government expenditure @@ -227,11 +228,11 @@ mystnb: width: 500px --- def compute_seign(x, α): - return np.exp(-α * x) - np.exp(-(1 + α) * x) + return np.exp(-α * x) - np.exp(-(1 + α) * x) def plot_laffer(model, πs): α, g = model.α, model.g - + # Generate π values x_values = np.linspace(0, 5, 1000) @@ -239,19 +240,18 @@ def plot_laffer(model, πs): y_values = compute_seign(x_values, α) # Plot the function - plt.plot(x_values, y_values, + plt.plot(x_values, y_values, label=f'$exp((-{α})x) - exp(- (1- {α}) x)$') for π, label in zip(πs, ['$\pi_l$', '$\pi_u$']): - plt.text(π, plt.gca().get_ylim()[0]*2, + plt.text(π, plt.gca().get_ylim()[0]*2, label, horizontalalignment='center', color='brown', size=10) plt.axvline(π, color='brown', linestyle='--') - plt.axhline(g, color='red', linewidth=0.5, + plt.axhline(g, color='red', linewidth=0.5, linestyle='--', label='g') plt.xlabel('$\pi$') plt.ylabel('seigniorage') plt.legend() - plt.grid(True) plt.show() # Steady state Laffer curve @@ -262,7 +262,7 @@ plot_laffer(model, (π_l, π_u)) Now that we have our hands on the two possible steady states, we can compute two initial log price levels $p_{-1}$, which as initial conditions, imply that $\pi_t = \bar \pi $ for all $t \geq 0$. -In particular, to initiate a fixed point of the dynamic Laffer curve dynamics, we set +In particular, to initiate a fixed point of the dynamic Laffer curve dynamics, we set $$ p_{-1} = m_0 + \alpha \pi^* @@ -279,7 +279,7 @@ p_l, p_u = map(lambda π: solve_p_init(model, π), (π_l, π_u)) print('Associated initial p_{-1}s', f'are: {p_l, p_u}') ``` -### Verification +### Verification To start, let's write some code to verify that if we initial $\pi_{-1}^*,p_{-1}$ appropriately, the inflation rate $\pi_t$ will be constant for all $t \geq 0$ (at either $\pi_u$ or $\pi_l$ depending on the initial condition) @@ -288,33 +288,33 @@ The following code verifies this. ```{code-cell} ipython3 def solve_laffer_adapt(p_init, π_init, model, num_steps): m0, α, δ, g = model.m0, model.α, model.δ, model.g - - m_seq = np.nan * np.ones(num_steps+1) - π_seq = np.nan * np.ones(num_steps) + + m_seq = np.nan * np.ones(num_steps+1) + π_seq = np.nan * np.ones(num_steps) p_seq = np.nan * np.ones(num_steps) - μ_seq = np.nan * np.ones(num_steps) - + μ_seq = np.nan * np.ones(num_steps) + m_seq[1] = m0 π_seq[0] = π_init p_seq[0] = p_init - + for t in range(1, num_steps): # Solve p_t def p_t(pt): return np.log(np.exp(m_seq[t]) + g * np.exp(pt)) \ - pt + α * ((1-δ)*(pt - p_seq[t-1]) + δ*π_seq[t-1]) - + p_seq[t] = root(fun=p_t, x0=p_seq[t-1]).x[0] - + # Solve π_t π_seq[t] = (1-δ) * (p_seq[t]-p_seq[t-1]) + δ*π_seq[t-1] - + # Solve m_t m_seq[t+1] = np.log(np.exp(m_seq[t]) + g*np.exp(p_seq[t])) - + # Solve μ_t μ_seq[t] = m_seq[t+1] - m_seq[t] - + return π_seq, μ_seq, m_seq, p_seq ``` @@ -323,7 +323,7 @@ Compute limiting values starting from $p_{-1}$ associated with $\pi_l$ ```{code-cell} ipython3 π_seq, μ_seq, m_seq, p_seq = solve_laffer_adapt(p_l, π_l, model, 50) -# Check steady state m_{t+1} - m_t and p_{t+1} - p_t +# Check steady state m_{t+1} - m_t and p_{t+1} - p_t print('m_{t+1} - m_t:', m_seq[-1] - m_seq[-2]) print('p_{t+1} - p_t:', p_seq[-1] - p_seq[-2]) @@ -338,7 +338,7 @@ Compute limiting values starting from $p_{-1}$ associated with $\pi_u$ ```{code-cell} ipython3 π_seq, μ_seq, m_seq, p_seq = solve_laffer_adapt(p_u, π_u, model, 50) -# Check steady state m_{t+1} - m_t and p_{t+1} - p_t +# Check steady state m_{t+1} - m_t and p_{t+1} - p_t print('m_{t+1} - m_t:', m_seq[-1] - m_seq[-2]) print('p_{t+1} - p_t:', p_seq[-1] - p_seq[-2]) @@ -350,7 +350,7 @@ print('eq_g == g:', np.isclose(eq_g(m_seq[-1] - m_seq[-2]), model.g)) ## Slippery side of Laffer curve dynamics -We are now equipped to compute time series starting from different $p_{-1}, \pi_{-1}^*$ settings, analogous to those in this lecture {doc}`money_inflation` and this lecture {doc}`money_inflation_nonlinear`. +We are now equipped to compute time series starting from different $p_{-1}, \pi_{-1}^*$ settings, analogous to those in this lecture {doc}`money_inflation` and this lecture {doc}`money_inflation_nonlinear`. Now we'll study how outcomes unfold when we start $p_{-1}, \pi_{-1}^*$ away from a stationary point of the dynamic Laffer curve, i.e., away from either $\pi_u$ or $ \pi_l$. @@ -367,7 +367,7 @@ def draw_iterations(π0s, model, line_params, π_bars, num_steps): for ax in axes[:2]: ax.set_yscale('log') - + for i, π0 in enumerate(π0s): p0 = model.m0 + model.α*π0 π_seq, μ_seq, m_seq, p_seq = solve_laffer_adapt(p0, π0, model, num_steps) @@ -376,12 +376,12 @@ def draw_iterations(π0s, model, line_params, π_bars, num_steps): axes[1].plot(np.arange(-1, num_steps-1), p_seq, **line_params) axes[2].plot(np.arange(-1, num_steps-1), π_seq, **line_params) axes[3].plot(np.arange(num_steps), μ_seq, **line_params) - + axes[2].axhline(y=π_bars[0], color='grey', linestyle='--', lw=1.5, alpha=0.6) axes[2].axhline(y=π_bars[1], color='grey', linestyle='--', lw=1.5, alpha=0.6) - axes[2].text(num_steps * 1.07, π_bars[0], r'$\pi_l$', verticalalignment='center', + axes[2].text(num_steps * 1.07, π_bars[0], r'$\pi_l$', verticalalignment='center', color='grey', size=10) - axes[2].text(num_steps * 1.07, π_bars[1], r'$\pi_u$', verticalalignment='center', + axes[2].text(num_steps * 1.07, π_bars[1], r'$\pi_u$', verticalalignment='center', color='grey', size=10) axes[0].set_ylabel('$m_t$') @@ -409,10 +409,166 @@ mystnb: --- πs = np.linspace(π_l, π_u, 10) -line_params = {'lw': 1.5, +line_params = {'lw': 1.5, 'marker': 'o', 'markersize': 3} - + π_bars = (π_l, π_u) draw_iterations(πs, model, line_params, π_bars, num_steps=80) ``` + +## Exercises + +```{exercise} +:label: la_ex1 + +**Comparative statics: how do steady-state inflation rates change with the government deficit $g$?** + +The lecture claims that, under adaptive expectations, the "old time religion" holds: +lowering the government deficit $g$ lowers the low-inflation steady state $\pi_l$. + +a. Compute the maximum seigniorage revenue $g_{\rm max}$ and the corresponding + $x_{\rm max}$ by finding the $x$ that maximises $\exp(-\alpha x) - \exp(-(1+\alpha)x)$ + using `scipy.optimize.minimize_scalar`. + +b. For $g$ ranging from a small positive value to $0.999 \times g_{\rm max}$, + compute both $\pi_l(g)$ and $\pi_u(g)$ and plot them against $g$ on the + same axes. + +c. Verify that the two roots merge as $g \to g_{\rm max}$ and that $\pi_l$ + falls as $g$ is reduced from the benchmark value $g = 0.35$ to $g/2$, then + relate this to the "old time religion" claim in the lecture. +``` + +```{solution-start} la_ex1 +:class: dropdown +``` + +```{code-cell} ipython3 +from scipy.optimize import minimize_scalar + +# Part a: find g_max +res = minimize_scalar(lambda x: -compute_seign(x, model.α), + bounds=(0, 10), method='bounded') +x_max = res.x +g_max = compute_seign(x_max, model.α) +print(f"x_max = {x_max:.4f}") +print(f"g_max = {g_max:.4f}") +``` + +```{code-cell} ipython3 +# Part b: trace π_l(g) and π_u(g) +g_grid = np.linspace(0.01, g_max * 0.999, 300) +πl_list, πu_list = [], [] + +for g in g_grid: + mod_g = create_model(g=g) + πl_list.append(solve_π_bar(mod_g, x0=0.3)) + πu_list.append(solve_π_bar(mod_g, x0=4.0)) + +fig, ax = plt.subplots() +ax.plot(g_grid, πl_list, label=r'$\pi_l(g)$ - low-inflation steady state') +ax.plot(g_grid, πu_list, label=r'$\pi_u(g)$ - high-inflation steady state') +ax.axvline(model.g, color='grey', linestyle='--', lw=1, + label=f'benchmark $g = {model.g}$') +ax.set_xlabel('government deficit $g$') +ax.set_ylabel('steady-state inflation $\\bar\\pi$') +ax.set_title('Steady-state inflation rates vs government deficit') +ax.legend() +plt.tight_layout() +plt.show() +``` + +```{code-cell} ipython3 +# Part c: Verify "old time religion" +π_l_bench = solve_π_bar(model, x0=0.3) +π_l_half = solve_π_bar(create_model(g=model.g / 2), x0=0.3) +print(f"π_l at g = {model.g:.2f}: {π_l_bench:.4f}") +print(f"π_l at g = {model.g/2:.3f}: {π_l_half:.4f}") +print(f"Cutting g in half reduces π_l by {π_l_bench - π_l_half:.4f}") +``` + +The two curves merge at $g_{\rm max}$ because the Laffer curve peaks there and +can no longer support two distinct inflation rates. + +As $g$ falls, $\pi_l$ falls monotonically while $\pi_u$ rises, confirming the "old time religion" that adaptive expectations select the low-inflation equilibrium where a lower deficit directly implies lower inflation. + +```{solution-end} +``` + +```{exercise} +:label: la_ex2 + +**How the speed of expectation adjustment $\delta$ affects convergence.** + +The parameter $\delta \in (0,1)$ controls how slowly the public updates its +inflation expectations: $\delta$ close to $1$ means expectations are very +sluggish (heavily backward-looking), while $\delta$ close to $0$ means they +adjust almost instantly. + +Fix an initial $\pi_0$ halfway between $\pi_l$ and $\pi_u$, i.e., +$\pi_0 = (\pi_l + \pi_u)/2$, and set $p_{-1} = m_0 + \alpha \pi_0$. + +a. Using `create_model` and `solve_laffer_adapt`, simulate 80 steps for each + $\delta \in \{0.3,\, 0.6,\, 0.9\}$, plot the resulting $\pi_t$ paths on a + single panel, and add a horizontal dashed line at $\pi_l$ for reference. + +b. For each $\delta$ value, report how many time steps it takes for $\pi_t$ + to come within $0.01$ of $\pi_l$. + +c. Explain intuitively why a larger $\delta$ leads to slower convergence. +``` + +```{solution-start} la_ex2 +:class: dropdown +``` + +```{code-cell} ipython3 +δ_values = [0.3, 0.6, 0.9] +num_steps = 80 +π0 = (π_l + π_u) / 2 # start midway between the two steady states + +fig, ax = plt.subplots(figsize=(8, 4)) + +for δ in δ_values: + mod_δ = create_model(δ=δ) + # Recompute steady states for this δ (they don't change, but confirm) + π_l_δ = solve_π_bar(mod_δ, x0=0.6) + p0 = mod_δ.m0 + mod_δ.α * π0 + π_seq, *_ = solve_laffer_adapt(p0, π0, mod_δ, num_steps) + ax.plot(np.arange(num_steps), π_seq, lw=1.5, marker='o', + markersize=2, label=f'$\\delta={δ}$') + +ax.axhline(π_l, color='grey', linestyle='--', lw=1.5, alpha=0.7, + label=r'$\pi_l$') +ax.set_xlabel('timestep') +ax.set_ylabel(r'$\pi_t$') +ax.set_title('Convergence to $\\pi_l$ for different adaptation speeds $\\delta$') +ax.legend() +plt.tight_layout() +plt.show() +``` + +```{code-cell} ipython3 +# Part b: steps to come within 0.01 of π_l +tol = 0.01 +print(f"{'δ':>5} {'steps to |π_t - π_l| < 0.01':>30}") +print('-' * 40) +for δ in δ_values: + mod_δ = create_model(δ=δ) + p0 = mod_δ.m0 + mod_δ.α * π0 + π_seq, *_ = solve_laffer_adapt(p0, π0, mod_δ, num_steps) + hits = np.where(np.abs(π_seq - π_l) < tol)[0] + steps = hits[0] if len(hits) > 0 else ">80" + print(f"{δ:>5} {str(steps):>30}") +``` + +**Part c.** When $\delta$ is large, each period's revision of $\pi_t^*$ is a small +fraction $(1-\delta)$ of the forecast error, so expectations are sticky. + +This means the expectations signal that drives the economy toward $\pi_l$ arrives only weakly each period, so the real inflation rate $\pi_t$ creeps toward the steady state rather than jumping there quickly. + +A small $\delta$ gives forecast errors full or near-full weight, snapping expectations to the current observation and pulling $\pi_t$ to $\pi_l$ within just a few periods. + +```{solution-end} +``` diff --git a/lectures/lake_model.md b/lectures/lake_model.md index f70da94f4..7a02138b1 100644 --- a/lectures/lake_model.md +++ b/lectures/lake_model.md @@ -128,8 +128,8 @@ class LakeModel: [ (1-d)*λ, (1-α)*(1-d)]]) - self.ū = (1 + g - (1 - d) * (1 - α)) / (1 + g - (1 - d) * (1 - α) + (1 - d) * λ) - self.ē = 1 - self.ū + self.u_bar = (1 + g - (1 - d) * (1 - α)) / (1 + g - (1 - d) * (1 - α) + (1 - d) * λ) + self.e_bar = 1 - self.u_bar def simulate_path(self, x0, T=1000): @@ -180,9 +180,6 @@ axes[1].set_title('Employment') axes[2].plot(x_path.sum(0), lw=2) axes[2].set_title('Labor force') -for ax in axes: - ax.grid() - plt.tight_layout() plt.show() ``` @@ -310,7 +307,7 @@ def plot_time_paths(lm, x0=None, T=1000, ax=None): if x0 is None: x0 = np.array([[5.0, 0.1]]) - ū, ē = lm.ū, lm.ē + u_bar, e_bar = lm.u_bar, lm.e_bar x0 = np.atleast_2d(x0) @@ -318,7 +315,7 @@ def plot_time_paths(lm, x0=None, T=1000, ax=None): fig, ax = plt.subplots(figsize=(10, 8)) # Plot line D s = 10 - ax.plot([0, s * ū], [0, s * ē], "k--", lw=1, label='set $D$') + ax.plot([0, s * u_bar], [0, s * e_bar], "k--", lw=1, label='set $D$') # Set the axes through the origin for spine in ["left", "bottom"]: @@ -351,9 +348,9 @@ def plot_time_paths(lm, x0=None, T=1000, ax=None): textcoords="offset points", arrowprops=dict(arrowstyle = "->")) - ax.plot([ū], [ē], "ko", ms=4, alpha=0.6) + ax.plot([u_bar], [e_bar], "ko", ms=4, alpha=0.6) ax.annotate(r'$\bar{x}$', - xy=(ū, ē), + xy=(u_bar, e_bar), xycoords="data", xytext=(20, -20), textcoords="offset points", @@ -472,9 +469,9 @@ rate_path = x_path / x_path.sum(0) fig, axes = plt.subplots(2, 1, figsize=(10, 8)) -# Plot steady ū and ē -axes[0].hlines(lm.ū, 0, T, 'r', '--', lw=2, label='ū') -axes[1].hlines(lm.ē, 0, T, 'r', '--', lw=2, label='ē') +# Plot steady-state rates +axes[0].hlines(lm.u_bar, 0, T, 'r', '--', lw=2, label='u_bar') +axes[1].hlines(lm.e_bar, 0, T, 'r', '--', lw=2, label='e_bar') titles = ['Unemployment rate', 'Employment rate'] locations = ['lower right', 'upper right'] @@ -483,7 +480,6 @@ locations = ['lower right', 'upper right'] for i, ax in enumerate(axes): ax.plot(rate_path[i, :], lw=2, alpha=0.6) ax.set_title(titles[i]) - ax.grid() ax.legend(loc=locations[i]) @@ -545,24 +541,37 @@ Eq. {eq}`steady_x` implies that the long-run unemployment rate will increase, an if $\alpha$ increases or $\lambda$ decreases. Suppose first that $\alpha=0.01, \lambda=0.1, d=0.02, b=0.025$. + Assume that $\alpha$ increases to $0.04$. -The below graph illustrates that the line $D$ shifts clockwise downward, which indicates that -the fraction of unemployment rises as the separation rate increases. +Then compare this with a decrease in $\lambda$ from $0.1$ to $0.04$. -```{code-cell} ipython3 -fig, ax = plt.subplots(figsize=(10, 8)) +The graphs show that both changes rotate the line $D$ clockwise downward, which indicates that the long-run unemployment rate rises. -lm = LakeModel(α=0.01, λ=0.1, d=0.02, b=0.025) -plot_time_paths(lm, ax=ax) -s=10 -ax.plot([0, s * lm.ū], [0, s * lm.ē], "k--", lw=1, label='set $D$, α=0.01') - -lm = LakeModel(α=0.04, λ=0.1, d=0.02, b=0.025) -plot_time_paths(lm, ax=ax) -ax.plot([0, s * lm.ū], [0, s * lm.ē], "r--", lw=1, label='set $D$, α=0.04') +```{code-cell} ipython3 +fig, axes = plt.subplots(1, 2, figsize=(12, 5)) +s = 10 + +def add_D_line(ax, lm, label, color): + ax.plot([0, s * lm.u_bar], [0, s * lm.e_bar], + color=color, linestyle='--', lw=2, label=label) + +lm_base = LakeModel(α=0.01, λ=0.1, d=0.02, b=0.025) +lm_high_α = LakeModel(α=0.04, λ=0.1, d=0.02, b=0.025) +plot_time_paths(lm_base, ax=axes[0]) +add_D_line(axes[0], lm_base, r'set $D$, $\alpha=0.01$', 'black') +plot_time_paths(lm_high_α, ax=axes[0]) +add_D_line(axes[0], lm_high_α, r'set $D$, $\alpha=0.04$', 'red') +axes[0].legend(loc='best') + +lm_low_λ = LakeModel(α=0.01, λ=0.04, d=0.02, b=0.025) +plot_time_paths(lm_base, ax=axes[1]) +add_D_line(axes[1], lm_base, r'set $D$, $\lambda=0.1$', 'black') +plot_time_paths(lm_low_λ, ax=axes[1]) +add_D_line(axes[1], lm_low_λ, r'set $D$, $\lambda=0.04$', 'red') +axes[1].legend(loc='best') -ax.legend(loc='best') +plt.tight_layout() plt.show() ``` diff --git a/lectures/linear_equations.md b/lectures/linear_equations.md index 4b9ccb0fb..c67437214 100644 --- a/lectures/linear_equations.md +++ b/lectures/linear_equations.md @@ -250,7 +250,6 @@ for spine in ['right', 'top']: ax.spines[spine].set_color('none') ax.set(xlim=(-2, 10), ylim=(-4, 4)) -# ax.grid() vecs = ((4, -2), (3, 3), (7, 1)) tags = ('(x1, x2)', '(y1, y2)', '(x1+x2, y1+y2)') colors = ('blue', 'green', 'red') @@ -1050,7 +1049,7 @@ $$ where $C$, $D$ and $h$ are given by {eq}`two_eq_demand_mat` and {eq}`two_eq_supply_mat`. -This equation is analogous to {eq}`la_se_inv` with $A = (C-D)^{-1}$, $b = h$, and $x = p$. +This equation is analogous to {eq}`la_se_inv` with $A = C-D$, $b = h$, and $x = p$. We can now solve for equilibrium prices with NumPy's `linalg` submodule. @@ -1232,7 +1231,7 @@ p The solution is given by: $$ -p_0 = 4.6925, \; p_1 = 7.0625 \;\; \text{and} \;\; p_2 = 7.675 +p_0 = 4.9625, \; p_1 = 7.0625 \;\; \text{and} \;\; p_2 = 7.675 $$ ```{solution-end} @@ -1317,7 +1316,7 @@ It can be verified that this system has no solutions. We will thus try to find the best approximate solution for $x$. -1. Use {eq}`least_squares` and matrix algebra to find the least squares solution $\hat{x}$. +1. Use the least-squares formula above and matrix algebra to find the least squares solution $\hat{x}$. 2. Find the least squares solution using `numpy.linalg.lstsq` and compare the results. ```{exercise-end} diff --git a/lectures/lln_clt.md b/lectures/lln_clt.md index 9ee46a6d6..af83388b2 100644 --- a/lectures/lln_clt.md +++ b/lectures/lln_clt.md @@ -15,9 +15,9 @@ kernelspec: ## Overview -This lecture illustrates two of the most important results in probability and statistics: +This lecture illustrates two of the most important results in probability and statistics: -1. the law of large numbers (LLN) and +1. the law of large numbers (LLN) and 2. the central limit theorem (CLT). These beautiful theorems lie behind many of the most fundamental results in @@ -65,10 +65,10 @@ We can think of drawing $X$ as tossing a biased coin where We set $X=1$ if the coin is "heads" and zero otherwise. -The (population) mean of $X$ is +The (population) mean of $X$ is $$ - \mathbb E X + \mathbb E X = 0 \cdot \mathbb P\{X=0\} + 1 \cdot \mathbb P\{X=1\} = \mathbb P\{X=1\} = p $$ ``` @@ -82,7 +82,7 @@ print(X) ``` In this setting, the LLN tells us if we flip the coin many times, the fraction -of heads that we see will be close to the mean $p$. +of heads that we see will be close to the mean $p$. We use $n$ to represent the number of times the coin is flipped. @@ -107,7 +107,7 @@ converges to the "population mean". Think of $X_1, \ldots, X_n$ as independent flips of the coin. -The population mean is the mean in an infinite sample, which equals the +The population mean is the mean in an infinite sample, which equals the expectation $\mathbb E X$. The sample mean of the draws $X_1, \ldots, X_n$ is @@ -139,16 +139,16 @@ distribution. These random variables can be continuous or discrete. -For simplicity we will +For simplicity we will -* assume they are continuous and +* assume they are continuous and * let $f$ denote their common density function The last statement means that for any $i$ in $\{1, \ldots, n\}$ and any numbers $a, b$, -$$ +$$ \mathbb P\{a \leq X_i \leq b\} = \int_a^b f(x) dx $$ @@ -182,7 +182,7 @@ If $X_1, \ldots, X_n$ are IID and $\mathbb E |X|$ is finite, then ``` ```` -Here +Here * IID means independent and identically distributed and * $\mathbb E |X| = \int_{-\infty}^\infty |x| f(x) dx$ @@ -217,9 +217,9 @@ itself a random variable. The reason $\bar X_n$ is a random variable is that it's a function of the random variables $X_1, \ldots, X_n$. -What we are going to do now is +What we are going to do now is -1. pick some fixed distribution to draw each $X_i$ from +1. pick some fixed distribution to draw each $X_i$ from 1. set $n$ to some large number and then repeat the following three instructions. @@ -256,13 +256,13 @@ def draw_means(X_distribution, # The distribution of each X_i Now we write a function to generate $m$ sample means and histogram them. ```{code-cell} ipython3 -def generate_histogram(X_distribution, n, m): +def generate_histogram(X_distribution, n, m): # Compute m sample means sample_means = np.empty(m) for j in range(m): - sample_means[j] = draw_means(X_distribution, n) + sample_means[j] = draw_means(X_distribution, n) # Generate a histogram @@ -271,7 +271,7 @@ def generate_histogram(X_distribution, n, m): μ = X_distribution.mean() # Get the population mean σ = X_distribution.std() # and the standard deviation ax.axvline(x=μ, ls="--", c="k", label=fr"$\mu = {μ}$") - + ax.set_xlim(μ - σ, μ + σ) ax.set_xlabel(r'$\bar X_n$', size=12) ax.set_ylabel('density', size=12) @@ -283,7 +283,7 @@ Now we call the function. ```{code-cell} ipython3 # pick a distribution to draw each $X_i$ from -X_distribution = st.norm(loc=5, scale=2) +X_distribution = st.norm(loc=5, scale=2) # Call the function generate_histogram(X_distribution, n=1_000, m=1000) ``` @@ -298,7 +298,7 @@ We will use a [violin plot](https://intro.quantecon.org/prob_dist.html#violin-pl Each distribution in the violin plot represents the distribution of $X_n$ for some $n$, calculated by simulation. ```{code-cell} ipython3 -def means_violin_plot(distribution, +def means_violin_plot(distribution, ns = [1_000, 10_000, 100_000], m = 10_000): @@ -474,7 +474,7 @@ $F(x) = 1 - e^{- \lambda x}$. # Set parameters n = 250 # Choice of n k = 1_000_000 # Number of draws of Y_n -distribution = st.expon(2) # Exponential distribution, λ = 1/2 +distribution = st.expon(scale=2) # Exponential distribution, λ = 1/2 μ, σ = distribution.mean(), distribution.std() # Draw underlying RVs. Each row contains a draw of X_1,..,X_n @@ -490,7 +490,7 @@ xmin, xmax = -3 * σ, 3 * σ ax.set_xlim(xmin, xmax) ax.hist(Y, bins=60, alpha=0.4, density=True) xgrid = np.linspace(xmin, xmax, 200) -ax.plot(xgrid, st.norm.pdf(xgrid, scale=σ), +ax.plot(xgrid, st.norm.pdf(xgrid, scale=σ), 'k-', lw=2, label=r'$N(0, \sigma^2)$') ax.set_xlabel(r"$Y_n$", size=12) ax.set_ylabel(r"$density$", size=12) @@ -509,7 +509,7 @@ The fit to the normal density is already tight and can be further improved by in -```{exercise} +```{exercise} :label: lln_ex1 Repeat the simulation [above](sim_one) with the [Beta distribution](https://en.wikipedia.org/wiki/Beta_distribution). @@ -552,7 +552,7 @@ plt.show() ```{solution-end} ``` -````{exercise} +````{exercise} :label: lln_ex2 At the start of this lecture we discussed Bernoulli random variables. @@ -592,14 +592,14 @@ This means that $X = \mathbf 1\{U < p\}$ has the right distribution. -```{exercise} +```{exercise} :label: lln_ex3 We mentioned above that LLN can still hold sometimes when IID is violated. Let's investigate this claim further. -Consider the AR(1) process +Consider the AR(1) process $$ X_{t+1} = \alpha + \beta X_t + \sigma \epsilon _{t+1} @@ -621,7 +621,7 @@ However, the next exercise teaches us that LLN type convergence of the sample mean to the population mean still occurs. 1. Prove that the sequence $X_1, X_2, \ldots$ is identically distributed. -2. Show that LLN convergence holds using simulations with $\alpha = 0.8$, $\beta = 0.2$. +2. Show that LLN convergence holds using simulations with $\alpha = 0.8$, $\beta = 0.2$, and $\sigma = 10$. ``` @@ -646,7 +646,7 @@ $$ &= \alpha + \beta \frac{\alpha}{1-\beta} \\ &= \frac{\alpha}{1-\beta} \end{aligned} -$$ +$$ We also have the correct variance: @@ -656,7 +656,7 @@ $$ &= \frac{\beta^2\sigma^2}{1-\beta^2} + \sigma^2 \\ &= \frac{\sigma^2}{1-\beta^2} \end{aligned} -$$ +$$ Finally, since both $X_t$ and $\epsilon_0$ are normally distributed and independent from each other, any linear combination of these two variables is @@ -665,9 +665,9 @@ also normally distributed. We have now shown that $$ - X_{t+1} \sim - N \left(\frac{\alpha}{1-\beta}, \frac{\sigma^2}{1-\beta^2}\right) -$$ + X_{t+1} \sim + N \left(\frac{\alpha}{1-\beta}, \frac{\sigma^2}{1-\beta^2}\right) +$$ We can conclude this AR(1) process violates the independence assumption but is identically distributed. @@ -682,7 +682,7 @@ n = 100_000 fig, ax = plt.subplots(figsize=(10, 6)) x = np.ones(n) -x[0] = st.norm.rvs(α/(1-β), α**2/(1-β**2)) +x[0] = st.norm.rvs(loc=α/(1-β), scale=σ / np.sqrt(1-β**2)) ϵ = st.norm.rvs(size=n+1) means = np.ones(n) means[0] = x[0] @@ -696,8 +696,8 @@ ax.scatter(range(100, n), means[100:n], s=10, alpha=0.5) ax.set_xlabel(r"$n$", size=12) ax.set_ylabel(r"$\bar X_n$", size=12) yabs_max = max(ax.get_ylim(), key=abs) -ax.axhline(y=α/(1-β), ls="--", lw=3, - label=r"$\mu = \frac{\alpha}{1-\beta}$", +ax.axhline(y=α/(1-β), ls="--", lw=3, + label=r"$\mu = \frac{\alpha}{1-\beta}$", color = 'black') plt.legend() diff --git a/lectures/long_run_growth.md b/lectures/long_run_growth.md index e21bee1c0..da137f764 100644 --- a/lectures/long_run_growth.md +++ b/lectures/long_run_growth.md @@ -19,7 +19,7 @@ In this lecture we use Python, {doc}`pandas`, and {doc}`Matplotli In addition to learning how to deploy these tools more generally, we'll use them to describe facts about economic growth experiences across many countries over several centuries. -Such "growth facts" are interesting for a variety of reasons. +Such "growth facts" are interesting for a variety of reasons. Explaining growth facts is a principal purpose of both "development economics" and "economic history". @@ -44,7 +44,7 @@ US GDP surpassed that of the British Empire. For Adam Tooze, that fact was a key geopolitical underpinning for the "American century". -Looking at this graph and how it set the geopolitical stage for "the American (20th) century" naturally +Looking at this graph and how it set the geopolitical stage for "the American (20th) century" naturally tempts one to want a counterpart to his graph for 2014 or later. (An impatient reader seeking a hint at the answer might now want to jump ahead and look at figure {numref}`gdp2`.) @@ -53,11 +53,11 @@ As we'll see, reasoning by analogy, this graph perhaps set the stage for an "XXX As we gather data to construct those two graphs, we'll also study growth experiences for a number of countries for time horizons extending as far back as possible. -These graphs will portray how the "Industrial Revolution" began in Britain in the late 18th century, then migrated to one country after another. +These graphs will portray how the "Industrial Revolution" began in Britain in the late 18th century, then migrated to one country after another. -In a nutshell, this lecture records growth trajectories of various countries over long time periods. +In a nutshell, this lecture records growth trajectories of various countries over long time periods. -While some countries have experienced long-term rapid growth that has lasted a hundred years, others have not. +While some countries have experienced long-term rapid growth that has lasted a hundred years, others have not. Since populations differ across countries and vary within a country over time, it will be interesting to describe both total GDP and GDP per capita as it evolves within a country. @@ -77,7 +77,7 @@ from collections import namedtuple A project initiated by [Angus Maddison](https://en.wikipedia.org/wiki/Angus_Maddison) has collected many historical time series related to economic growth, some dating back to the first century. -The data can be downloaded from the [Maddison Historical Statistics](https://www.rug.nl/ggdc/historicaldevelopment/maddison/) by clicking on the "Latest Maddison Project Release". +The data can be downloaded from the [Maddison Historical Statistics](https://www.rug.nl/ggdc/historicaldevelopment/maddison/) by clicking on the "Latest Maddison Project Release". We are going to read the data from a QuantEcon GitHub repository. @@ -87,7 +87,7 @@ Here we read the Maddison data into a pandas `DataFrame`: ```{code-cell} ipython3 data_url = "https://github.com/QuantEcon/lecture-python-intro/raw/main/lectures/datasets/mpd2020.xlsx" -data = pd.read_excel(data_url, +data = pd.read_excel(data_url, sheet_name='Full data') data.head() ``` @@ -101,7 +101,7 @@ countries = data.country.unique() len(countries) ``` -We can now explore some of the 169 countries that are available. +We can now explore some of the 169 countries that are available. Let's loop over each country to understand which years are available for each country @@ -147,7 +147,7 @@ country_names = data['countrycode'] colors = cm.tab20(np.linspace(0, 0.95, len(country_names))) # Create a dictionary to map each country to its corresponding color -color_mapping = {country: color for +color_mapping = {country: color for country, color in zip(country_names, colors)} ``` @@ -178,7 +178,9 @@ gdp_pc[country].plot( ``` :::{note} -[International dollars](https://en.wikipedia.org/wiki/international_dollar) are a hypothetical unit of currency that has the same purchasing power parity that the U.S. Dollar has in the United States at a given point in time. They are also known as Geary–Khamis dollars (GK Dollars). +[International dollars](https://en.wikipedia.org/wiki/international_dollar) are a hypothetical unit of currency that has the same purchasing power parity that the U.S. Dollar has in the United States at a given point in time. + +They are also known as Geary-Khamis dollars (GK Dollars). ::: We can see that the data is incomplete for longer periods in the early 250 years of this millennium, so we could choose to interpolate to get a continuous line plot. @@ -243,19 +245,19 @@ def draw_interp_plots(series, # pandas series color=color_mapping[c], alpha=0.8, label=code_to_name.loc[c]['country']) - + if logscale: ax.set_yscale('log') - + # Draw the legend outside the plot ax.legend(loc='upper left', frameon=False) ax.set_ylabel(ylabel) ax.set_xlabel(xlabel) ``` -As you can see from this chart, economic growth started in earnest in the 18th century and continued for the next two hundred years. +As you can see from this chart, economic growth started in earnest in the 18th century and continued for the next two hundred years. -How does this compare with other countries' growth trajectories? +How does this compare with other countries' growth trajectories? Let's look at the United States (USA), United Kingdom (GBR), and China (CHN) @@ -273,7 +275,7 @@ Event = namedtuple('Event', ['year_range', 'y_text', 'text', 'color', 'ymax']) fig, ax = plt.subplots(dpi=300, figsize=(10, 6)) country = ['CHN', 'GBR', 'USA'] -draw_interp_plots(gdp_pc[country].loc[1500:], +draw_interp_plots(gdp_pc[country].loc[1500:], country, 'international dollars','year', color_mapping, code_to_name, 2, False, ax) @@ -281,31 +283,31 @@ draw_interp_plots(gdp_pc[country].loc[1500:], # Define the parameters for the events and the text ylim = ax.get_ylim()[1] b_params = {'color':'grey', 'alpha': 0.2} -t_params = {'fontsize': 9, +t_params = {'fontsize': 9, 'va':'center', 'ha':'center'} # Create a list of events to annotate events = [ - Event((1650, 1652), ylim + ylim*0.04, + Event((1650, 1652), ylim + ylim*0.04, 'the Navigation Act\n(1651)', color_mapping['GBR'], 1), - Event((1655, 1684), ylim + ylim*0.13, - 'Closed-door Policy\n(1655-1684)', + Event((1655, 1684), ylim + ylim*0.13, + 'Closed-door Policy\n(1655-1684)', color_mapping['CHN'], 1.1), Event((1848, 1850), ylim + ylim*0.22, - 'the Repeal of Navigation Act\n(1849)', + 'the Repeal of Navigation Act\n(1849)', color_mapping['GBR'], 1.18), - Event((1765, 1791), ylim + ylim*0.04, - 'American Revolution\n(1765-1791)', + Event((1765, 1791), ylim + ylim*0.04, + 'American Revolution\n(1765-1791)', color_mapping['USA'], 1), - Event((1760, 1840), ylim + ylim*0.13, - 'Industrial Revolution\n(1760-1840)', + Event((1760, 1840), ylim + ylim*0.13, + 'Industrial Revolution\n(1760-1840)', 'grey', 1.1), - Event((1929, 1939), ylim + ylim*0.04, - 'the Great Depression\n(1929–1939)', + Event((1929, 1939), ylim + ylim*0.04, + 'the Great Depression\n(1929-1939)', 'grey', 1), - Event((1978, 1979), ylim + ylim*0.13, - 'Reform and Opening-up\n(1978-1979)', + Event((1978, 1979), ylim + ylim*0.13, + 'Reform and Opening-up\n(1978-1979)', color_mapping['CHN'], 1.1) ] @@ -313,8 +315,8 @@ def draw_events(events, ax): # Iterate over events and add annotations and vertical lines for event in events: event_mid = sum(event.year_range)/2 - ax.text(event_mid, - event.y_text, event.text, + ax.text(event_mid, + event.y_text, event.text, color=event.color, **t_params) ax.axvspan(*event.year_range, color=event.color, alpha=0.2) ax.axvline(event_mid, ymin=1, ymax=event.ymax, color=event.color, @@ -326,7 +328,7 @@ plt.show() ``` The preceding graph of per capita GDP strikingly reveals how the spread of the Industrial Revolution has over time gradually lifted the living standards of substantial -groups of people +groups of people - most of the growth happened in the past 150 years after the Industrial Revolution. - per capita GDP in the US and UK rose and diverged from that of China from 1820 to 1940. @@ -339,7 +341,7 @@ It is fascinating to see China's GDP per capita levels from 1500 through to the Notice the long period of declining GDP per capital levels from the 1700s until the early 20th century. -Thus, the graph indicates +Thus, the graph indicates - a long economic downturn and stagnation after the Closed-door Policy by the Qing government. - China's very different experience than the UK's after the onset of the industrial revolution in the UK. @@ -357,7 +359,7 @@ tags: [hide-input] fig, ax = plt.subplots(dpi=300, figsize=(10, 6)) country = ['CHN'] -draw_interp_plots(gdp_pc[country].loc[1600:2000], +draw_interp_plots(gdp_pc[country].loc[1600:2000], country, 'international dollars','year', color_mapping, code_to_name, 2, True, ax) @@ -365,29 +367,29 @@ draw_interp_plots(gdp_pc[country].loc[1600:2000], ylim = ax.get_ylim()[1] events = [ -Event((1655, 1684), ylim + ylim*0.06, - 'Closed-door Policy\n(1655-1684)', +Event((1655, 1684), ylim + ylim*0.06, + 'Closed-door Policy\n(1655-1684)', 'tab:orange', 1), -Event((1760, 1840), ylim + ylim*0.06, - 'Industrial Revolution\n(1760-1840)', +Event((1760, 1840), ylim + ylim*0.06, + 'Industrial Revolution\n(1760-1840)', 'grey', 1), -Event((1839, 1842), ylim + ylim*0.2, - 'First Opium War\n(1839–1842)', +Event((1839, 1842), ylim + ylim*0.2, + 'First Opium War\n(1839-1842)', 'tab:red', 1.07), -Event((1861, 1895), ylim + ylim*0.4, - 'Self-Strengthening Movement\n(1861–1895)', +Event((1861, 1895), ylim + ylim*0.4, + 'Self-Strengthening Movement\n(1861-1895)', 'tab:blue', 1.14), -Event((1939, 1945), ylim + ylim*0.06, - 'WW 2\n(1939-1945)', +Event((1939, 1945), ylim + ylim*0.06, + 'WW 2\n(1939-1945)', 'tab:red', 1), -Event((1948, 1950), ylim + ylim*0.23, - 'Founding of PRC\n(1949)', +Event((1948, 1950), ylim + ylim*0.23, + 'Founding of PRC\n(1949)', color_mapping['CHN'], 1.08), -Event((1958, 1962), ylim + ylim*0.5, - 'Great Leap Forward\n(1958-1962)', +Event((1958, 1962), ylim + ylim*0.5, + 'Great Leap Forward\n(1958-1962)', 'tab:orange', 1.18), -Event((1978, 1979), ylim + ylim*0.7, - 'Reform and Opening-up\n(1978-1979)', +Event((1978, 1979), ylim + ylim*0.7, + 'Reform and Opening-up\n(1978-1979)', 'tab:blue', 1.24) ] @@ -400,7 +402,7 @@ plt.show() Now we look at the United States (USA) and United Kingdom (GBR) in more detail. -In the following graph, please watch for +In the following graph, please watch for - impact of trade policy (Navigation Act). - productivity changes brought by the Industrial Revolution. - how the US gradually approaches and then surpasses the UK, setting the stage for the ''American Century''. @@ -427,29 +429,29 @@ ylim = ax.get_ylim()[1] # Create a list of data points events = [ - Event((1651, 1651), ylim + ylim*0.15, - 'Navigation Act (UK)\n(1651)', + Event((1651, 1651), ylim + ylim*0.15, + 'Navigation Act (UK)\n(1651)', 'tab:orange', 1), - Event((1765, 1791), ylim + ylim*0.15, + Event((1765, 1791), ylim + ylim*0.15, 'American Revolution\n(1765-1791)', color_mapping['USA'], 1), - Event((1760, 1840), ylim + ylim*0.6, - 'Industrial Revolution\n(1760-1840)', + Event((1760, 1840), ylim + ylim*0.6, + 'Industrial Revolution\n(1760-1840)', 'grey', 1.08), - Event((1848, 1850), ylim + ylim*1.1, - 'Repeal of Navigation Act (UK)\n(1849)', + Event((1848, 1850), ylim + ylim*1.1, + 'Repeal of Navigation Act (UK)\n(1849)', 'tab:blue', 1.14), - Event((1861, 1865), ylim + ylim*1.8, - 'American Civil War\n(1861-1865)', + Event((1861, 1865), ylim + ylim*1.8, + 'American Civil War\n(1861-1865)', color_mapping['USA'], 1.21), - Event((1914, 1918), ylim + ylim*0.15, - 'WW 1\n(1914-1918)', + Event((1914, 1918), ylim + ylim*0.15, + 'WW 1\n(1914-1918)', 'tab:red', 1), - Event((1929, 1939), ylim + ylim*0.6, - 'the Great Depression\n(1929–1939)', + Event((1929, 1939), ylim + ylim*0.6, + 'the Great Depression\n(1929-1939)', 'grey', 1.08), - Event((1939, 1945), ylim + ylim*1.1, - 'WW 2\n(1939-1945)', + Event((1939, 1945), ylim + ylim*1.1, + 'WW 2\n(1939-1945)', 'tab:red', 1.14) ] @@ -491,7 +493,7 @@ mystnb: fig, ax = plt.subplots(dpi=300) country = ['CHN', 'SUN', 'JPN', 'GBR', 'USA'] start_year, end_year = (1820, 1945) -draw_interp_plots(gdp[country].loc[start_year:end_year], +draw_interp_plots(gdp[country].loc[start_year:end_year], country, 'international dollars', 'year', color_mapping, code_to_name, 2, False, ax) @@ -524,7 +526,7 @@ code_to_name = pd.concat([code_to_name, bem]) fig, ax = plt.subplots(dpi=300) country = ['DEU', 'USA', 'SUN', 'BEM', 'FRA', 'JPN'] start_year, end_year = (1821, 1945) -draw_interp_plots(gdp[country].loc[start_year:end_year], +draw_interp_plots(gdp[country].loc[start_year:end_year], country, 'international dollars', 'year', color_mapping, code_to_name, 2, False, ax) @@ -555,7 +557,7 @@ mystnb: fig, ax = plt.subplots(dpi=300) country = ['CHN', 'SUN', 'JPN', 'GBR', 'USA'] start_year, end_year = (1950, 2020) -draw_interp_plots(gdp[country].loc[start_year:end_year], +draw_interp_plots(gdp[country].loc[start_year:end_year], country, 'international dollars', 'year', color_mapping, code_to_name, 2, False, ax) @@ -570,8 +572,8 @@ We often want to study the historical experiences of countries outside the club The [Maddison Historical Statistics](https://www.rug.nl/ggdc/historicaldevelopment/maddison/) dataset also includes regional aggregations ```{code-cell} ipython3 -data = pd.read_excel(data_url, - sheet_name='Regional data', +data = pd.read_excel(data_url, + sheet_name='Regional data', header=(0,1,2), index_col=0) data.columns = data.columns.droplevel(level=2) diff --git a/lectures/lp_intro.md b/lectures/lp_intro.md index 52504a5aa..0b61a1e5d 100644 --- a/lectures/lp_intro.md +++ b/lectures/lp_intro.md @@ -41,7 +41,7 @@ We provide a standard form of a linear program and methods to transform other fo We tell how to solve a linear programming problem using [SciPy](https://scipy.org/) and [Google OR-Tools](https://developers.google.com/optimization). ```{seealso} -In another lecture, we will employ the linear programming method to solve the +In another lecture, we will employ the linear programming method to solve the {doc}`optimal transport problem `. ``` @@ -326,9 +326,9 @@ if status == pywraplp.Solver.OPTIMAL: print('Objective value =', solver.Objective().Value()) x1_sol = round(x1.solution_value(), 3) x2_sol = round(x2.solution_value(), 3) - x3_sol = round(x1.solution_value(), 3) - x4_sol = round(x2.solution_value(), 3) - x5_sol = round(x1.solution_value(), 3) + x3_sol = round(x3.solution_value(), 3) + x4_sol = round(x4.solution_value(), 3) + x5_sol = round(x5.solution_value(), 3) print(f'(x1, x2, x3, x4, x5): ({x1_sol}, {x2_sol}, {x3_sol}, {x4_sol}, {x5_sol})') else: print('The problem does not have an optimal solution.') @@ -338,11 +338,11 @@ OR-Tools tells us that the best investment strategy is: 1. At the beginning of the first year, the mutual fund should buy $ \$24,927.755$ of the annuity. Its bank account balance should be $ \$75,072.245$. -2. At the beginning of the second year, the mutual fund should buy $ \$24,927.755$ of the corporate bond and keep invest in the annuity. Its bank balance should be $ \$24,927.755$. +2. At the beginning of the second year, the mutual fund should buy $ \$4,648.825$ of the corporate bond and borrow $ \$20,000$ from the bank. -3. At the beginning of the third year, the bank balance should be $ \$75,072.245 $. +3. At the beginning of the third year, the bank balance should be $ \$50,000$. -4. At the end of the third year, the mutual fund will get payouts from the annuity and corporate bond and repay its loan from the bank. At the end it will own $ \$141,018.24 $, so that it's total net rate of return over the three periods is $ 41.02\%$. +4. At the end of the third year, the mutual fund will get payouts from the annuity and corporate bond and repay its loan from the bank, leaving it with $ \$141,018.24$ and a total net rate of return over the three periods of $41.02\%$. diff --git a/lectures/markov_chains_I.md b/lectures/markov_chains_I.md index 631dd70fb..ab47822ab 100644 --- a/lectures/markov_chains_I.md +++ b/lectures/markov_chains_I.md @@ -976,7 +976,7 @@ The red, yellow, and green dots represent different initial probability distribu The blue dot represents the unique stationary distribution. -Unlike Hamilton’s Markov chain, these initial distributions do not converge to the unique stationary distribution. +Unlike Hamilton's Markov chain, these initial distributions do not converge to the unique stationary distribution. Instead, they cycle periodically around the probability simplex, illustrating that asymptotic stability fails. @@ -1063,7 +1063,7 @@ $$ \sum_{j=0}^\infty \beta^j h(X_{t+j}) \mid X_t = x \right] - = x + \beta (Ph)(x) + \beta^2 (P^2 h)(x) + \cdots + = h(x) + \beta (Ph)(x) + \beta^2 (P^2 h)(x) + \cdots $$ By the {ref}`Neumann series lemma `, this sum can be calculated using @@ -1094,7 +1094,7 @@ where rows, from top to down, correspond to growth, stagnation, and collapse. In this exercise, 1. visualize the transition matrix and show this process is asymptotically stationary -1. calculate the stationary distribution using simulations +1. calculate the stationary distribution using matrix powers and QuantEcon package 1. visualize the dynamics of $(\psi_0 P^t)(i)$ where $t \in 0, ..., 25$ and compare the convergent path with the previous transition matrix Compare your solution to the paper. @@ -1171,7 +1171,7 @@ In this exercise, Solution 1: -Although $P$ is not every positive, $P^m$ when $m=3$ is everywhere positive. +Although $P$ is not everywhere positive, $P^m$ when $m=3$ is everywhere positive. ```{code-cell} ipython3 P = np.array([[0.86, 0.11, 0.03, 0.00, 0.00, 0.00], diff --git a/lectures/money_inflation.md b/lectures/money_inflation.md index 420b237f0..7de54f547 100644 --- a/lectures/money_inflation.md +++ b/lectures/money_inflation.md @@ -16,17 +16,17 @@ kernelspec: ## Overview This lecture extends and modifies the model in this lecture {doc}`cagan_ree` by modifying the -law of motion that governed the supply of money. +law of motion that governed the supply of money. The model in this lecture consists of two components -* a demand function for money +* a demand function for money * a law of motion for the supply of money - + The demand function describes the public's demand for "real balances", defined as the ratio of nominal money balances to the price level * it assumes that the demand for real balance today varies inversely with the rate of inflation that the public forecasts to prevail between today and tomorrow -* it assumes that the public's forecast of that rate of inflation is perfect +* it assumes that the public's forecast of that rate of inflation is perfect The law of motion for the supply of money assumes that the government prints money to finance government expenditures @@ -35,7 +35,7 @@ Our model equates the demand for money to the supply at each time $t \geq 0$. Equality between those demands and supply gives a *dynamic* model in which money supply and price level *sequences* are simultaneously determined by a set of simultaneous linear equations. -These equations take the form of what is often called vector linear **difference equations**. +These equations take the form of what is often called vector linear **difference equations**. In this lecture, we'll roll up our sleeves and solve those equations in two different ways. @@ -47,15 +47,15 @@ In this lecture we will encounter these concepts from macroeconomics: * an **inflation tax** that a government gathers by printing paper or electronic money * a dynamic **Laffer curve** in the inflation tax rate that has two stationary equilibria * perverse dynamics under rational expectations in which the system converges to the higher stationary inflation tax rate -* a peculiar comparative stationary-state outcome connected with that stationary inflation rate: it asserts that inflation can be *reduced* by running *higher* government deficits, i.e., by raising more resources by printing money. +* a peculiar comparative stationary-state outcome connected with that stationary inflation rate: it asserts that inflation can be *reduced* by running *higher* government deficits, i.e., by raising more resources by printing money. -The same qualitative outcomes prevail in this lecture {doc}`money_inflation_nonlinear` that studies a nonlinear version of the model in this lecture. +The same qualitative outcomes prevail in this lecture {doc}`money_inflation_nonlinear` that studies a nonlinear version of the model in this lecture. These outcomes set the stage for the analysis to be presented in this lecture {doc}`laffer_adaptive` that studies a nonlinear version of the present model; it assumes a version of "adaptive expectations" instead of rational expectations. -That lecture will show that +That lecture will show that -* replacing rational expectations with adaptive expectations leaves the two stationary inflation rates unchanged, but that $\ldots$ +* replacing rational expectations with adaptive expectations leaves the two stationary inflation rates unchanged, but that $\ldots$ * it reverses the perverse dynamics by making the *lower* stationary inflation rate the one to which the system typically converges * a more plausible comparative dynamic outcome emerges in which now inflation can be *reduced* by running *lower* government deficits @@ -72,26 +72,26 @@ We'll use these tools from linear algebra: We say demand*s* and suppl*ies* (plurals) because there is one of each for each $t \geq 0$. -Let +Let * $m_{t+1}$ be the supply of currency at the end of time $t \geq 0$ * $m_{t}$ be the supply of currency brought into time $t$ from time $t-1$ * $g$ be the government deficit that is financed by printing currency at $t \geq 1$ * $m_{t+1}^d$ be the demand at time $t$ for currency to bring into time $t+1$ * $p_t$ be the price level at time $t$ -* $b_t = \frac{m_{t+1}}{p_t}$ is real balances at the end of time $t$ +* $b_t = \frac{m_{t+1}}{p_t}$ is real balances at the end of time $t$ * $R_t = \frac{p_t}{p_{t+1}} $ be the gross rate of return on currency held from time $t$ to time $t+1$ - + It is often helpful to state units in which quantities are measured: * $m_t$ and $m_t^d$ are measured in dollars -* $g$ is measured in time $t$ goods +* $g$ is measured in time $t$ goods * $p_t$ is measured in dollars per time $t$ goods * $R_t$ is measured in time $t+1$ goods per unit of time $t$ goods * $b_t$ is measured in time $t$ goods - - -Our job now is to specify demand and supply functions for money. + + +Our job now is to specify demand and supply functions for money. We assume that the demand for currency satisfies the Cagan-like demand function @@ -99,20 +99,20 @@ $$ \frac{m_{t+1}^d}{p_t}=\gamma_1 - \gamma_2 \frac{p_{t+1}}{p_t}, \quad t \geq 0 $$ (eq:demandmoney) where $\gamma_1, \gamma_2$ are positive parameters. - + Now we turn to the supply of money. -We assume that $m_0 >0$ is an "initial condition" determined outside the model. +We assume that $m_0 >0$ is an "initial condition" determined outside the model. We set $m_0$ at some arbitrary positive value, say \$100. - + For $ t \geq 1$, we assume that the supply of money is determined by the government's budget constraint $$ m_{t+1} - m_{t} = p_t g , \quad t \geq 0 $$ (eq:budgcontraint) -According to this equation, each period, the government prints money to pay for quantity $g$ of goods. +According to this equation, each period, the government prints money to pay for quantity $g$ of goods. In an **equilibrium**, the demand for currency equals the supply: @@ -145,25 +145,25 @@ $$ b_t - b_{t-1} R_{t-1} = g $$ (eq:bmotion) -The demand for real balances is +The demand for real balances is $$ -b_t = \gamma_1 - \gamma_2 R_t^{-1} . +b_t = \gamma_1 - \gamma_2 R_t^{-1} . $$ (eq:bdemand) - + We'll restrict our attention to parameter values and associated gross real rates of return on real balances that assure that the demand for real balances is positive, which according to {eq}`eq:bdemand` means that $$ -b_t = \gamma_1 - \gamma_2 R_t^{-1} > 0 -$$ +b_t = \gamma_1 - \gamma_2 R_t^{-1} > 0 +$$ -which implies that +which implies that $$ R_t \geq \left( \frac{\gamma_2}{\gamma_1} \right) \equiv \underline R $$ (eq:Requation) -Gross real rate of return $\underline R$ is the smallest rate of return on currency +Gross real rate of return $\underline R$ is the smallest rate of return on currency that is consistent with a nonnegative demand for real balances. We shall describe two distinct but closely related ways of computing a pair $\{p_t, m_t\}_{t=0}^\infty$ of sequences for the price level and money supply. @@ -172,15 +172,15 @@ But first it is instructive to describe a special type of equilibrium known as a In a steady-state equilibrium, a subset of key variables remain constant or **invariant** over time, while remaining variables can be expressed as functions of those constant variables. -Finding such state variables is something of an art. +Finding such state variables is something of an art. -In many models, a good source of candidates for such invariant variables is a set of *ratios*. +In many models, a good source of candidates for such invariant variables is a set of *ratios*. This is true in the present model. ### Steady states -In a steady-state equilibrium of the model we are studying, +In a steady-state equilibrium of the model we are studying, $$ \begin{aligned} @@ -189,7 +189,7 @@ b_t & = \bar b \end{aligned} $$ -for $t \geq 0$. +for $t \geq 0$. Notice that both $R_t = \frac{p_t}{p_{t+1}}$ and $b_t = \frac{m_{t+1}}{p_t} $ are *ratios*. @@ -209,7 +209,7 @@ $$ $$ (eq:seignsteady) -The left side is the steady-state amount of **seigniorage** or government revenues that the government gathers by paying a gross rate of return $\bar R \le 1$ on currency. +The left side is the steady-state amount of **seigniorage** or government revenues that the government gathers by paying a gross rate of return $\bar R \le 1$ on currency. The right side is government expenditures. @@ -219,17 +219,17 @@ $$ S(\bar R) = (\gamma_1 + \gamma_2) - \frac{\gamma_2}{\bar R} - \gamma_1 \bar R $$ (eq:SSsigng) -Notice that $S(\bar R) \geq 0$ only when $\bar R \in [\frac{\gamma_2}{\gamma_1}, 1] +Notice that $S(\bar R) \geq 0$ only when $\bar R \in [\frac{\gamma_2}{\gamma_1}, 1] \equiv [\underline R, \overline R]$ and that $S(\bar R) = 0$ if $\bar R = \underline R$ or if $\bar R = \overline R$. We shall study equilibrium sequences that satisfy $$ -R_t \in [\underline R, \overline R], \quad t \geq 0. +R_t \in [\underline R, \overline R], \quad t \geq 0. $$ -Maximizing steady-state seigniorage {eq}`eq:SSsigng` with respect to $\bar R$, we find that the maximizing rate of return on currency is +Maximizing steady-state seigniorage {eq}`eq:SSsigng` with respect to $\bar R$, we find that the maximizing rate of return on currency is $$ \bar R_{\rm max} = \sqrt{\frac{\gamma_2}{\gamma_1}} @@ -249,7 +249,7 @@ $$ (eq:steadyquadratic) A steady state gross rate of return $\bar R$ solves quadratic equation {eq}`eq:steadyquadratic`. -So two steady states typically exist. +So two steady states typically exist. ## Some code @@ -269,17 +269,17 @@ First, we create a `namedtuple` to store parameters so that we can reuse this `n ```{code-cell} ipython3 # Create a namedtuple that contains parameters -MoneySupplyModel = namedtuple("MoneySupplyModel", - ["γ1", "γ2", "g", +MoneySupplyModel = namedtuple("MoneySupplyModel", + ["γ1", "γ2", "g", "M0", "R_u", "R_l"]) def create_model(γ1=100, γ2=50, g=3.0, M0=100): - + # Calculate the steady states for R R_steady = np.roots((-γ1, γ1 + γ2 - g, -γ2)) R_u, R_l = R_steady print("[R_u, R_l] =", R_steady) - + return MoneySupplyModel(γ1=γ1, γ2=γ2, g=g, M0=M0, R_u=R_u, R_l=R_l) ``` @@ -357,20 +357,20 @@ We now proceed to compute equilibria, not necessarily steady states. We shall deploy two distinct computation strategies. -### Method 1 +### Method 1 * set $R_0 \in [\frac{\gamma_2}{\gamma_1}, R_u]$ and compute $b_0 = \gamma_1 - \gamma_2/R_0$. -* compute sequences $\{R_t, b_t\}_{t=1}^\infty$ of rates of return and real balances that are associated with an equilibrium by solving equation {eq}`eq:bmotion` and {eq}`eq:bdemand` sequentially for $t \geq 1$: - +* compute sequences $\{R_t, b_t\}_{t=1}^\infty$ of rates of return and real balances that are associated with an equilibrium by solving equation {eq}`eq:bmotion` and {eq}`eq:bdemand` sequentially for $t \geq 1$: + $$ \begin{aligned} b_t & = b_{t-1} R_{t-1} + g \cr -R_t^{-1} & = \frac{\gamma_1}{\gamma_2} - \gamma_2^{-1} b_t +R_t^{-1} & = \frac{\gamma_1}{\gamma_2} - \gamma_2^{-1} b_t \end{aligned} $$ (eq:rtbt) -* Construct the associated equilibrium $p_0$ from +* Construct the associated equilibrium $p_0$ from $$ p_0 = \frac{m_0}{\gamma_1 - g - \gamma_2/R_0} @@ -381,10 +381,10 @@ $$ (eq:p0fromR0) $$ \begin{aligned} p_t & = R_t p_{t-1} \cr -m_t & = b_{t-1} p_t +m_t & = b_{t-1} p_t \end{aligned} -$$ (eq:method1) - +$$ (eq:method1) + ```{prf:remark} :label: method_1 Method 1 uses an indirect approach to computing an equilibrium by first computing an equilibrium $\{R_t, b_t\}_{t=0}^\infty$ sequence and then using it to back out an equilibrium $\{p_t, m_t\}_{t=0}^\infty$ sequence. @@ -392,7 +392,11 @@ Method 1 uses an indirect approach to computing an equilibrium by first computin ```{prf:remark} :label: initial_condition -Notice that method 1 starts by picking an **initial condition** $R_0$ from a set $[\frac{\gamma_2}{\gamma_1}, R_u]$. Equilibrium $\{p_t, m_t\}_{t=0}^\infty$ sequences are not unique. There is actually a continuum of equilibria indexed by a choice of $R_0$ from the set $[\frac{\gamma_2}{\gamma_1}, R_u]$. +Notice that method 1 starts by picking an **initial condition** $R_0$ from a set $[\frac{\gamma_2}{\gamma_1}, R_u]$. + +Equilibrium $\{p_t, m_t\}_{t=0}^\infty$ sequences are not unique. + +There is actually a continuum of equilibria indexed by a choice of $R_0$ from the set $[\frac{\gamma_2}{\gamma_1}, R_u]$. ``` ```{prf:remark} @@ -400,11 +404,11 @@ Notice that method 1 starts by picking an **initial condition** $R_0$ from a se Associated with each selection of $R_0$ there is a unique $p_0$ described by equation {eq}`eq:p0fromR0`. ``` - + ### Method 2 -This method deploys a direct approach. -It defines a "state vector" +This method deploys a direct approach. +It defines a "state vector" $y_t = \begin{bmatrix} m_t \cr p_t\end{bmatrix} $ and formulates equilibrium conditions {eq}`eq:demandmoney`, {eq}`eq:budgcontraint`, and {eq}`eq:syeqdemand` @@ -414,66 +418,69 @@ $$ y_{t+1} = M y_t, \quad t \geq 0 , $$ -where we temporarily take $y_0 = \begin{bmatrix} m_0 \cr p_0 \end{bmatrix}$ as an **initial condition**. +where we temporarily take $y_0 = \begin{bmatrix} m_0 \cr p_0 \end{bmatrix}$ as an **initial condition**. -The solution is +The solution is $$ y_t = M^t y_0 . $$ -Now let's think about the initial condition $y_0$. +Now let's think about the initial condition $y_0$. It is natural to take the initial stock of money $m_0 >0$ as an initial condition. -But what about $p_0$? +But what about $p_0$? Isn't it something that we want to be *determined* by our model? -Yes, but sometimes we want too much, because there is actually a continuum of initial $p_0$ levels that are compatible with the existence of an equilibrium. +Yes, but sometimes we want too much, because there is actually a continuum of initial $p_0$ levels that are compatible with the existence of an equilibrium. + +As we shall see soon, selecting an initial $p_0$ in method 2 is intimately tied to selecting an initial rate of return on currency $R_0$ in method 1. -As we shall see soon, selecting an initial $p_0$ in method 2 is intimately tied to selecting an initial rate of return on currency $R_0$ in method 1. - -## Computation method 1 +## Computation method 1 -%We start from an arbitrary $R_0$ and $b_t = \frac{m_{t+1}}{p_t}$, we have +%We start from an arbitrary $R_0$ and $b_t = \frac{m_{t+1}}{p_t}$, we have %$$ -%b_0 = \gamma_1 - \gamma_0 R_0^{-1} +%b_0 = \gamma_1 - \gamma_0 R_0^{-1} %$$ Remember that there exist two steady-state equilibrium values $ R_\ell < R_u$ of the rate of return on currency $R_t$. We proceed as follows. -Start at $t=0$ -* select a $R_0 \in [\frac{\gamma_2}{\gamma_1}, R_u]$ -* compute $b_0 = \gamma_1 - \gamma_0 R_0^{-1} $ - +Start at $t=0$ +* select a $R_0 \in [\frac{\gamma_2}{\gamma_1}, R_u]$ +* compute $b_0 = \gamma_1 - \gamma_0 R_0^{-1} $ + Then for $t \geq 1$ construct $b_t, R_t$ by iterating on equation {eq}`eq:rtbt`. -When we implement this part of method 1, we shall discover the following striking +When we implement this part of method 1, we shall discover the following striking outcome: -* starting from an $R_0$ in $[\frac{\gamma_2}{\gamma_1}, R_u]$, we shall find that +* starting from an $R_0$ in $[\frac{\gamma_2}{\gamma_1}, R_u]$, we shall find that $\{R_t\}$ always converges to a limiting "steady state" value $\bar R$ that depends on the initial condition $R_0$. -* there are only two possible limit points $\{ R_\ell, R_u\}$. +* there are only two possible limit points $\{ R_\ell, R_u\}$. * for almost every initial condition $R_0$, $\lim_{t \rightarrow +\infty} R_t = R_\ell$. * if and only if $R_0 = R_u$, $\lim_{t \rightarrow +\infty} R_t = R_u$. - + The quantity $1 - R_t$ can be interpreted as an **inflation tax rate** that the government imposes on holders of its currency. We shall soon see that the existence of two steady-state rates of return on currency -that serve to finance the government deficit of $g$ indicates the presence of a **Laffer curve** in the inflation tax rate. +that serve to finance the government deficit of $g$ indicates the presence of a **Laffer curve** in the inflation tax rate. ```{note} -Arthur Laffer's curve plots a hump shaped curve of revenue raised from a tax against the tax rate. -Its hump shape indicates that there are typically two tax rates that yield the same amount of revenue. This is due to two countervailing courses, one being that raising a tax rate typically decreases the **base** of the tax as people take decisions to reduce their exposure to the tax. +Arthur Laffer's curve plots a hump shaped curve of revenue raised from a tax against the tax rate. + +Its hump shape indicates that there are typically two tax rates that yield the same amount of revenue. + +This is due to two countervailing courses, one being that raising a tax rate typically decreases the **base** of the tax as people take decisions to reduce their exposure to the tax. ``` ```{code-cell} ipython3 @@ -502,43 +509,43 @@ Let's write some code to plot outcomes for several possible initial values $R_0$ ```{code-cell} ipython3 :tags: [hide-cell] -line_params = {'lw': 1.5, +line_params = {'lw': 1.5, 'marker': 'o', 'markersize': 3} def annotate_graph(ax, model, num_steps): - for y, label in [(model.R_u, '$R_u$'), (model.R_l, '$R_l$'), + for y, label in [(model.R_u, '$R_u$'), (model.R_l, '$R_l$'), (model.γ2 / model.γ1, r'$\frac{\gamma_2}{\gamma_1}$')]: ax.axhline(y=y, color='grey', linestyle='--', lw=1.5, alpha=0.6) - ax.text(num_steps * 1.02, y, label, verticalalignment='center', + ax.text(num_steps * 1.02, y, label, verticalalignment='center', color='grey', size=12) def draw_paths(R0_values, model, line_params, num_steps): fig, axes = plt.subplots(2, 1, figsize=(8, 8), sharex=True) - + # Pre-compute time steps - time_steps = np.arange(num_steps) - - # Iterate over R_0s and simulate the system + time_steps = np.arange(num_steps) + + # Iterate over R_0s and simulate the system for R0 in R0_values: b_values, R_values = simulate_system(R0, model, num_steps) - + # Plot R_t against time axes[0].plot(time_steps, R_values, **line_params) - + # Plot b_t against time axes[1].plot(time_steps, b_values, **line_params) - - # Add line and text annotations to the subgraph + + # Add line and text annotations to the subgraph annotate_graph(axes[0], model, num_steps) - + # Add Labels axes[0].set_ylabel('$R_t$') axes[1].set_xlabel('timestep') axes[1].set_ylabel('$b_t$') axes[1].xaxis.set_major_locator(MaxNLocator(integer=True)) - + plt.tight_layout() plt.show() ``` @@ -564,11 +571,11 @@ draw_paths(R0s, msm, line_params, num_steps=20) Notice how sequences that start from $R_0$ in the half-open interval $[R_\ell, R_u)$ converge to the steady state associated with to $ R_\ell$. -## Computation method 2 +## Computation method 2 -Set $m_t = m_t^d $ for all $t \geq -1$. +Set $m_t = m_t^d $ for all $t \geq -1$. -Let +Let $$ y_t = \begin{bmatrix} m_{t} \cr p_{t} \end{bmatrix} . @@ -580,30 +587,30 @@ $$ \begin{bmatrix} 1 & \gamma_2 \cr 1 & 0 \end{bmatrix} \begin{bmatrix} m_{t+1} \cr p_{t+1} \end{bmatrix} = \begin{bmatrix} 0 & \gamma_1 \cr - 1 & g \end{bmatrix} \begin{bmatrix} m_{t} \cr p_{t} \end{bmatrix} + 1 & g \end{bmatrix} \begin{bmatrix} m_{t} \cr p_{t} \end{bmatrix} $$ (eq:sytem101) or -$$ -H_1 y_t = H_2 y_{t-1} +$$ +H_1 y_t = H_2 y_{t-1} $$ -where +where $$ \begin{aligned} H_1 & = \begin{bmatrix} 1 & \gamma_2 \cr 1 & 0 \end{bmatrix} \cr H_2 & = \begin{bmatrix} 0 & \gamma_1 \cr - 1 & g \end{bmatrix} + 1 & g \end{bmatrix} \end{aligned} $$ ```{code-cell} ipython3 -H1 = np.array([[1, msm.γ2], +H1 = np.array([[1, msm.γ2], [1, 0]]) -H2 = np.array([[0, msm.γ1], - [1, msm.g]]) +H2 = np.array([[0, msm.γ1], + [1, msm.g]]) ``` Define @@ -620,7 +627,7 @@ print('H = \n', H) and write the system {eq}`eq:sytem101` as $$ -y_{t+1} = H y_t, \quad t \geq 0 +y_{t+1} = H y_t, \quad t \geq 0 $$ (eq:Vaughn) so that $\{y_t\}_{t=0}$ can be computed from @@ -629,7 +636,7 @@ $$ y_t = H^t y_0, t \geq 0 $$ (eq:ytiterate) -where +where $$ y_0 = \begin{bmatrix} m_{0} \cr p_0 \end{bmatrix} . @@ -642,20 +649,20 @@ it is something that we actually wanted to be determined by the model. (As usual, we should listen when mathematics talks to us.) -For now, let's just proceed mechanically on faith. +For now, let's just proceed mechanically on faith. -Compute the eigenvector decomposition +Compute the eigenvector decomposition $$ -H = Q \Lambda Q^{-1} -$$ +H = Q \Lambda Q^{-1} +$$ where $\Lambda$ is a diagonal matrix of eigenvalues and the columns of $Q$ are eigenvectors corresponding to those eigenvalues. -It turns out that +It turns out that $$ -\Lambda = \begin{bmatrix} {R_\ell}^{-1} & 0 \cr +\Lambda = \begin{bmatrix} {R_\ell}^{-1} & 0 \cr 0 & {R_u}^{-1} \end{bmatrix} $$ @@ -677,30 +684,30 @@ print(f'R_u = {R_u:.4f}') Partition $Q$ as -$$ +$$ Q =\begin{bmatrix} Q_{11} & Q_{12} \cr Q_{21} & Q_{22} \end{bmatrix} $$ -Below we shall verify the following claims: +Below we shall verify the following claims: -**Claims:** If we set +**Claims:** If we set $$ p_0 = \overline p_0 \equiv Q_{21} Q_{11}^{-1} m_{0} , $$ (eq:magicp0) -it turns out that +it turns out that -$$ +$$ \frac{p_{t+1}}{p_t} = {R_u}^{-1}, \quad t \geq 0 $$ -However, if we set +However, if we set -$$ +$$ p_0 > \bar p_0 $$ @@ -732,7 +739,7 @@ def iterate_H(y_0, H, num_steps): Q_inv = np.linalg.inv(Q) y = np.stack( [Q @ np.diag(Λ**t) @ Q_inv @ y_0 for t in range(num_steps)], 1) - + return y ``` @@ -744,10 +751,10 @@ To understand this situation, we use the following transformation $$ -y^*_t = Q^{-1} y_t . +y^*_t = Q^{-1} y_t . $$ -Dynamics of $y^*_t$ are evidently governed by +Dynamics of $y^*_t$ are evidently governed by $$ y^*_{t+1} = \Lambda^t y^*_t . @@ -755,7 +762,7 @@ $$ (eq:stardynamics) This equation represents the dynamics of our system in a way that lets us isolate the force that causes gross inflation to converge to the inverse of the lower steady-state rate -of inflation $R_\ell$ that we discovered earlier. +of inflation $R_\ell$ that we discovered earlier. Staring at equation {eq}`eq:stardynamics` indicates that unless @@ -766,11 +773,11 @@ y^*_0 = \begin{bmatrix} y^*_{1,0} \cr 0 \end{bmatrix} ``` the path of $y^*_t$, and therefore the paths of both $m_t$ and $p_t$ given by -$y_t = Q y^*_t$ will eventually grow at gross rates ${R_\ell}^{-1}$ as -$t \rightarrow +\infty$. +$y_t = Q y^*_t$ will eventually grow at gross rates ${R_\ell}^{-1}$ as +$t \rightarrow +\infty$. Equation {eq}`equation_11` also leads us to conclude that there is a unique setting -for the initial vector $y_0$ for which both components forever grow at the lower rate ${R_u}^{-1}$. +for the initial vector $y_0$ for which both components forever grow at the lower rate ${R_u}^{-1}$. For this to occur, the required setting of $y_0$ must evidently have the property @@ -821,7 +828,7 @@ p_0 = - (Q^{22})^{-1} Q^{21} m_0. ``` -### More convenient formula +### More convenient formula We can get the equivalent but perhaps more convenient formula {eq}`eq:magicp0` for $p_0$ that is cast in terms of components of $Q$ instead of components of @@ -880,10 +887,10 @@ We create a function `draw_iterations` to generate the plot def draw_iterations(p0s, model, line_params, num_steps): fig, axes = plt.subplots(3, 1, figsize=(8, 10), sharex=True) - + # Pre-compute time steps - time_steps = np.arange(num_steps) - + time_steps = np.arange(num_steps) + # Plot the first two y-axes in log scale for ax in axes[:2]: ax.set_yscale('log') @@ -899,20 +906,20 @@ def draw_iterations(p0s, model, line_params, num_steps): # Plot b_t against time axes[1].plot(time_steps, P, **line_params) - + # Calculate R_t R = np.insert(P[:-1] / P[1:], 0, np.nan) axes[2].plot(time_steps, R, **line_params) - - # Add line and text annotations to the subgraph + + # Add line and text annotations to the subgraph annotate_graph(axes[2], model, num_steps) - + # Draw labels axes[0].set_ylabel('$m_t$') axes[1].set_ylabel('$p_t$') axes[2].set_ylabel('$R_t$') axes[2].set_xlabel('timestep') - + # Enforce integar axis label axes[2].xaxis.set_major_locator(MaxNLocator(integer=True)) @@ -935,7 +942,7 @@ p0s = [p0_bar, 2.34, 2.5, 3, 4, 7, 30, 100_000] draw_iterations(p0s, msm, line_params, num_steps=20) ``` -Please notice that for $m_t$ and $p_t$, we have used log scales for the coordinate (i.e., vertical) axes. +Please notice that for $m_t$ and $p_t$, we have used log scales for the coordinate (i.e., vertical) axes. Using log scales allows us to spot distinct constant limiting gross rates of growth ${R_u}^{-1}$ and ${R_\ell}^{-1}$ by eye. @@ -948,13 +955,13 @@ As promised at the start of this lecture, we have encountered these concepts fro * an **inflation tax** that a government gathers by printing paper or electronic money * a dynamic **Laffer curve** in the inflation tax rate that has two stationary equilibria -Staring at the paths of rates of return on the price level in figure {numref}`R0_path` and price levels in {numref}`p0_path` show indicate that almost all paths converge to the *higher* inflation tax rate displayed in the stationary state Laffer curve displayed in figure {numref}`infl_tax`. +Staring at the paths of rates of return on the price level in figure {numref}`R0_path` and price levels in {numref}`p0_path` show indicate that almost all paths converge to the *higher* inflation tax rate displayed in the stationary state Laffer curve displayed in figure {numref}`infl_tax`. Thus, we have indeed discovered what we earlier called "perverse" dynamics under rational expectations in which the system converges to the higher of two possible stationary inflation tax rates. Those dynamics are "perverse" not only in the sense that they imply that the monetary and fiscal authorities that have chosen to finance government expenditures eventually impose a higher inflation tax than required to finance government expenditures, but because of the following "counterintuitive" situation that we can deduce by staring at the stationary state Laffer curve displayed in figure {numref}`infl_tax`: -* the figure indicates that inflation can be *reduced* by running *higher* government deficits, i.e., by raising more resources through printing money. +* the figure indicates that inflation can be *reduced* by running *higher* government deficits, i.e., by raising more resources through printing money. ```{note} @@ -962,25 +969,265 @@ The same qualitative outcomes prevail in this lecture {doc}`money_inflation_nonl ``` -## Equilibrium selection +## Equilibrium selection We have discovered that as a model of price level paths or model is **incomplete** because there is a continuum of "equilibrium" paths for $\{m_{t+1}, p_t\}_{t=0}^\infty$ that are consistent with the demand for real balances always equaling the supply. - + Through application of our computational methods 1 and 2, we have learned that this continuum can be indexed by choice of one of two scalars: -* for computational method 1, $R_0$ +* for computational method 1, $R_0$ * for computational method 2, $p_0$ -To apply our model, we have somehow to *complete* it by *selecting* an equilibrium path from among the continuum of possible paths. +To apply our model, we have somehow to *complete* it by *selecting* an equilibrium path from among the continuum of possible paths. -We discovered that +We discovered that * all but one of the equilibrium paths converge to limits in which the higher of two possible stationary inflation tax prevails * there is a unique equilibrium path associated with "plausible" statements about how reductions in government deficits affect a stationary inflation rate -On grounds of plausibility, we recommend following many macroeconomists in selecting the unique equilibrium that converges to the lower stationary inflation tax rate. +On grounds of plausibility, we recommend following many macroeconomists in selecting the unique equilibrium that converges to the lower stationary inflation tax rate. As we shall see, we shall accept this recommendation in lecture {doc}`unpleasant`. In lecture, {doc}`laffer_adaptive`, we shall explore how {cite}`bruno1990seigniorage` and others justified this in other ways. + +## Exercises + +```{exercise} +:label: mi_ex1 + +**The seigniorage Laffer curve: peak revenue and fiscal limits.** + +The lecture states that steady-state seigniorage + +$$ +S(\bar R) = (\gamma_1 + \gamma_2) - \frac{\gamma_2}{\bar R} - \gamma_1 \bar R +$$ + +is maximized at $\bar R_{\rm max} = \sqrt{\gamma_2/\gamma_1}$. + +a. Verify this analytically by differentiating $S(\bar R)$ with respect to + $\bar R$, setting the derivative to zero, and solving for $\bar R$. + +b. Using the default model `msm`, compute $\bar R_{\rm max}$ and the + corresponding maximum revenue $g_{\rm max} = S(\bar R_{\rm max})$, then + plot the seigniorage curve together with a horizontal line at $g_{\rm max}$. + +c. Evaluate the discriminant of the steady-state quadratic + {eq}`eq:steadyquadratic` for $g = g_{\rm max} + 1$ and explain + what happens if the government tries to finance a deficit $g > g_{\rm max}$. +``` + +```{solution-start} mi_ex1 +:class: dropdown +``` + +**Part a.** Differentiating with respect to $\bar R$: + +$$ +S'(\bar R) = \frac{\gamma_2}{\bar R^2} - \gamma_1 = 0 +\quad \Longrightarrow \quad +\bar R^2 = \frac{\gamma_2}{\gamma_1} +\quad \Longrightarrow \quad +\bar R_{\rm max} = \sqrt{\frac{\gamma_2}{\gamma_1}}. +$$ + +Since $S''(\bar R) = -2\gamma_2/\bar R^3 < 0$, this is indeed a maximum. + +```{code-cell} ipython3 +γ1, γ2 = msm.γ1, msm.γ2 + +R_max = np.sqrt(γ2 / γ1) +g_max = seign(R_max, msm) +print(f"R_max = sqrt(γ2/γ1) = sqrt({γ2}/{γ1}) = {R_max:.4f}") +print(f"g_max = S(R_max) = {g_max:.4f}") + +R_plot = np.linspace(γ2/γ1, 1, 300) +fig, ax = plt.subplots() +ax.plot(R_plot, seign(R_plot, msm), label='$S(\\bar R)$') +ax.axhline(g_max, color='red', linestyle='--', label=f'$g_{{\\rm max}}={g_max:.2f}$') +ax.axvline(R_max, color='grey', linestyle=':', lw=1) +ax.set_xlabel('$\\bar R$') +ax.set_ylabel('seigniorage') +ax.set_title('Seigniorage Laffer curve and peak revenue') +ax.legend() +plt.tight_layout() +plt.show() +``` + +**Part c.** The steady-state quadratic is $-\gamma_1 \bar R^2 + (\gamma_1+\gamma_2-g)\bar R - \gamma_2 = 0$. + +Its discriminant is $(\gamma_1+\gamma_2-g)^2 - 4\gamma_1\gamma_2$. + +```{code-cell} ipython3 +g_too_high = g_max + 1 +discriminant = (γ1 + γ2 - g_too_high)**2 - 4 * γ1 * γ2 +roots = np.roots((-γ1, γ1 + γ2 - g_too_high, -γ2)) +print(f"g = g_max + 1 = {g_too_high:.4f}") +print(f"Discriminant = {discriminant:.4f} ({'negative' if discriminant < 0 else 'positive'})") +print(f"np.roots = {roots}") +print(f"Roots are real: {np.all(np.isreal(roots))}") +``` + +When $g > g_{\rm max}$ the discriminant is negative and there are no real +steady-state rates of return on currency. + +Economically, the government is trying to finance a deficit that exceeds the maximum amount of seigniorage the inflation tax can ever raise, regardless of the inflation rate chosen. + +No stationary equilibrium exists. + +```{solution-end} +``` + +```{exercise} +:label: mi_ex2 + +**How steady-state rates of return vary with the government deficit.** + +The two steady-state roots $R_l < R_u$ of the quadratic +{eq}`eq:steadyquadratic` depend on the government deficit $g$. + +a. For $g$ ranging from a value near $0$ to just below $g_{\rm max}$, + compute both roots $R_l(g)$ and $R_u(g)$ and plot them against $g$ on + the same graph. + +b. Verify the following boundary conditions analytically and numerically: + + * At $g = 0$: the two roots should be $R = 1$ and $R = \gamma_2/\gamma_1$. + * As $g \to g_{\rm max}$: the two roots should merge at $\bar R_{\rm max} = \sqrt{\gamma_2/\gamma_1}$. + +c. Mark the benchmark deficit $g = 3$ on your graph, read off $R_u$ and $R_l$, + and check whether your graph values agree with `msm.R_u` and `msm.R_l`. +``` + +```{solution-start} mi_ex2 +:class: dropdown +``` + +```{code-cell} ipython3 +R_max = np.sqrt(msm.γ2 / msm.γ1) +g_max = seign(R_max, msm) + +g_grid = np.linspace(1e-6, g_max * (1 - 1e-4), 300) +R_u_curve, R_l_curve = [], [] + +for g in g_grid: + roots = np.sort(np.roots((-msm.γ1, msm.γ1 + msm.γ2 - g, -msm.γ2)).real) + R_l_curve.append(roots[0]) + R_u_curve.append(roots[1]) + +fig, ax = plt.subplots() +ax.plot(g_grid, R_u_curve, label='$R_u(g)$ - low inflation steady state') +ax.plot(g_grid, R_l_curve, label='$R_l(g)$ - high inflation steady state') +ax.axvline(msm.g, color='grey', linestyle='--', lw=1, + label=f'benchmark $g = {msm.g}$') +ax.set_xlabel('government deficit $g$') +ax.set_ylabel('steady-state rate of return $\\bar R$') +ax.set_title('Steady-state rates of return vs government deficit') +ax.legend() +plt.tight_layout() +plt.show() + +# Boundary conditions +print("Boundary checks:") +print(f" g -> 0: R_u -> {R_u_curve[0]:.4f} (expected 1.0)") +print(f" R_l -> {R_l_curve[0]:.4f} (expected γ2/γ1 = {msm.γ2/msm.γ1:.4f})") +print(f" g -> g_max: R_u -> {R_u_curve[-1]:.4f}") +print(f" R_l -> {R_l_curve[-1]:.4f}") +print(f" R_max = {R_max:.4f} (roots should merge here)") +print(f"\nAt benchmark g = {msm.g}:") +print(f" R_u from curve = {R_u_curve[np.argmin(np.abs(g_grid - msm.g))]:.4f}, " + f"msm.R_u = {msm.R_u:.4f}") +print(f" R_l from curve = {R_l_curve[np.argmin(np.abs(g_grid - msm.g))]:.4f}, " + f"msm.R_l = {msm.R_l:.4f}") +``` + +The two branches of the Laffer curve start apart at $g=0$ and merge at +$g = g_{\rm max}$. + +For $g > g_{\rm max}$ no real steady state exists. + +The upper branch $R_u(g)$ falls and the lower branch $R_l(g)$ rises as +$g$ increases, reflecting the increasing inflation tax needed to finance a +larger deficit. + +```{solution-end} +``` + +```{exercise} +:label: mi_ex3 + +**Quantity theory of money via the eigendecomposition.** + +Method 2 identifies a unique "magic" initial price level + +$$ +\bar p_0 = \frac{Q_{21}}{Q_{11}} m_0 +$$ + +(where $Q_{ij}$ denotes the $(i,j)$ element of the eigenvector matrix $Q$) +that places the economy on the low-inflation equilibrium path. + +a. Using `iterate_H`, simulate the path of $y_t = (m_t, p_t)$ starting from + $y_0 = (m_0,\, \bar p_0)$, compute $R_t = p_t/p_{t+1}$ for $t = 0, \ldots, 10$, + and verify that it equals `msm.R_u` at every step. + +b. Use the formula $\bar p_0 = (Q_{21}/Q_{11})\, m_0$ to interpret the initial + price level as proportional to the money supply, compute $\bar p_0$ for + $m_0 \in [50, 300]$, plot $\bar p_0$ against $m_0$, and report the slope. + +c. Set $R_0 = R_u$ in the Method 1 formula for $p_0$ from equation + {eq}`eq:p0fromR0` and confirm that you recover $\bar p_0$. +``` + +```{solution-start} mi_ex3 +:class: dropdown +``` + +```{code-cell} ipython3 +# Part a: verify R_t = R_u along the magic-p0 path +p0_bar = (Q[1, 0] / Q[0, 0]) * msm.M0 +y0 = np.array([msm.M0, p0_bar]) +num_steps = 12 + +y_series = iterate_H(y0, H, num_steps) +P = y_series[1, :] +R_path = P[:-1] / P[1:] # R_t = p_t / p_{t+1} + +print(f"R_t along the magic-p0 path (first {num_steps-1} periods):") +print(np.round(R_path, 6)) +print(f"msm.R_u = {msm.R_u:.6f}") +``` + +```{code-cell} ipython3 +# Part b: plot p0_bar vs m0 +m0_values = np.linspace(50, 300, 80) +p0_bar_values = (Q[1, 0] / Q[0, 0]) * m0_values + +fig, ax = plt.subplots() +ax.plot(m0_values, p0_bar_values) +ax.set_xlabel('$m_0$') +ax.set_ylabel('$\\bar p_0$') +ax.set_title('Quantity theory: $\\bar p_0$ proportional to $m_0$') +plt.tight_layout() +plt.show() + +slope = Q[1, 0] / Q[0, 0] +print(f"Slope = Q_21 / Q_11 = {slope:.6f}") + +# Part c: compare with Method 1 formula eq:p0fromR0 +p0_method1 = msm.M0 / (msm.γ1 - msm.g - msm.γ2 / msm.R_u) +print(f"\nMethod 1 formula (R_0 = R_u): p0 = {p0_method1:.6f}") +print(f"Eigendecomposition formula: p0 = {p0_bar:.6f}") +``` + +Parts a and c confirm that both methods select exactly the same unique +initial price level. + +Part b shows the quantity-theory proportionality that doubling $m_0$ exactly doubles $\bar p_0$, with constant slope $Q_{21}/Q_{11}$. + +This linearity is a direct consequence of the linearity of the model. + +```{solution-end} +``` diff --git a/lectures/money_inflation_nonlinear.md b/lectures/money_inflation_nonlinear.md index bce94f096..cc41f3ea1 100644 --- a/lectures/money_inflation_nonlinear.md +++ b/lectures/money_inflation_nonlinear.md @@ -11,59 +11,59 @@ kernelspec: name: python3 --- -# Inflation Rate Laffer Curves +# Inflation Rate Laffer Curves ## Overview We study stationary and dynamic *Laffer curves* in the inflation tax rate in a non-linear version of the model studied in {doc}`money_inflation`. We use the log-linear version of the demand function for money that {cite}`Cagan` -used in his classic paper in place of the linear demand function used in {doc}`money_inflation`. +used in his classic paper in place of the linear demand function used in {doc}`money_inflation`. That change requires that we modify parts of our analysis. -In particular, our dynamic system is no longer linear in state variables. +In particular, our dynamic system is no longer linear in state variables. -Nevertheless, the economic logic underlying an analysis based on what we called ''method 2'' remains unchanged. +Nevertheless, the economic logic underlying an analysis based on what we called ''method 2'' remains unchanged. We shall discover qualitatively similar outcomes to those that we studied in {doc}`money_inflation`. -That lecture presented a linear version of the model in this lecture. +That lecture presented a linear version of the model in this lecture. As in that lecture, we discussed these topics: * an **inflation tax** that a government gathers by printing paper or electronic money * a dynamic **Laffer curve** in the inflation tax rate that has two stationary equilibria * perverse dynamics under rational expectations in which the system converges to the higher stationary inflation tax rate -* a peculiar comparative stationary-state analysis connected with that stationary inflation rate that asserts that inflation can be *reduced* by running *higher* government deficits +* a peculiar comparative stationary-state analysis connected with that stationary inflation rate that asserts that inflation can be *reduced* by running *higher* government deficits These outcomes will set the stage for the analysis of {doc}`laffer_adaptive` that studies a version of the present model that uses a version of "adaptive expectations" instead of rational expectations. -That lecture will show that +That lecture will show that -* replacing rational expectations with adaptive expectations leaves the two stationary inflation rates unchanged, but that $\ldots$ +* replacing rational expectations with adaptive expectations leaves the two stationary inflation rates unchanged, but that $\ldots$ * it reverses the perverse dynamics by making the *lower* stationary inflation rate the one to which the system typically converges * a more plausible comparative dynamic outcome emerges in which now inflation can be *reduced* by running *lower* government deficits ## The Model -Let +Let * $m_t$ be the log of the money supply at the beginning of time $t$ * $p_t$ be the log of the price level at time $t$ - -The demand function for money is + +The demand function for money is $$ -m_{t+1} - p_t = -\alpha (p_{t+1} - p_t) +m_{t+1} - p_t = -\alpha (p_{t+1} - p_t) $$ (eq:mdemand) -where $\alpha \geq 0$. +where $\alpha \geq 0$. The law of motion of the money supply is -$$ -\exp(m_{t+1}) - \exp(m_t) = g \exp(p_t) +$$ +\exp(m_{t+1}) - \exp(m_t) = g \exp(p_t) $$ (eq:msupply) where $g$ is the part of government expenditures financed by printing money. @@ -79,7 +79,7 @@ Please notice that while equation {eq}`eq:mdemand` is linear in logs of the mone We can compute the two prospective limiting values for $\overline \pi$ by studying the steady-state Laffer curve. -Thus, in a *steady state* +Thus, in a *steady state* $$ m_{t+1} - m_t = p_{t+1} - p_t = x \quad \forall t , @@ -90,20 +90,20 @@ where $x > 0 $ is a common rate of growth of logarithms of the money supply and A few lines of algebra yields the following equation that $x$ satisfies $$ -\exp(-\alpha x) - \exp(-(1 + \alpha) x) = g +\exp(-\alpha x) - \exp(-(1 + \alpha) x) = g $$ (eq:steadypi) where we require that $$ -g \leq \max_{x \geq 0} \{\exp(-\alpha x) - \exp(-(1 + \alpha) x) \}, +g \leq \max_{x \geq 0} \{\exp(-\alpha x) - \exp(-(1 + \alpha) x) \}, $$ (eq:revmax) so that it is feasible to finance $g$ by printing money. The left side of {eq}`eq:steadypi` is steady state revenue raised by printing money. -The right side of {eq}`eq:steadypi` is the quantity of time $t$ goods that the government raises by printing money. +The right side of {eq}`eq:steadypi` is the quantity of time $t$ goods that the government raises by printing money. Soon we'll plot the left and right sides of equation {eq}`eq:steadypi`. @@ -117,7 +117,7 @@ from collections import namedtuple import numpy as np import matplotlib.pyplot as plt from matplotlib.ticker import MaxNLocator -from scipy.optimize import fsolve +from scipy.optimize import fsolve ``` +++ {"user_expressions": []} @@ -125,7 +125,7 @@ from scipy.optimize import fsolve Let's create a `namedtuple` to store the parameters of the model ```{code-cell} ipython3 -CaganLaffer = namedtuple('CaganLaffer', +CaganLaffer = namedtuple('CaganLaffer', ["m0", # log of the money supply at t=0 "α", # sensitivity of money demand "λ", @@ -173,11 +173,11 @@ mystnb: --- def compute_seign(x, α): - return np.exp(-α * x) - np.exp(-(1 + α) * x) + return np.exp(-α * x) - np.exp(-(1 + α) * x) def plot_laffer(model, πs): α, g = model.α, model.g - + # Generate π values x_values = np.linspace(0, 5, 1000) @@ -185,14 +185,14 @@ def plot_laffer(model, πs): y_values = compute_seign(x_values, α) # Plot the function - plt.plot(x_values, y_values, + plt.plot(x_values, y_values, label=f'Laffer curve') for π, label in zip(πs, [r'$\pi_l$', r'$\pi_u$']): - plt.text(π, plt.gca().get_ylim()[0]*2, + plt.text(π, plt.gca().get_ylim()[0]*2, label, horizontalalignment='center', color='brown', size=10) plt.axvline(π, color='brown', linestyle='--') - plt.axhline(g, color='red', linewidth=0.5, + plt.axhline(g, color='red', linewidth=0.5, linestyle='--', label='g') plt.xlabel(r'$\pi$') plt.ylabel('seigniorage') @@ -210,7 +210,7 @@ $\overline p(m_0)$, which as initial conditions for $p_t$ at time $t$, imply tha The function $\underline p(m_0)$ will be associated with $\pi_l$ the lower steady-state inflation rate. -The function $\overline p(m_0)$ will be associated with $\pi_u$ the lower steady-state inflation rate. +The function $\overline p(m_0)$ will be associated with $\pi_u$ the higher steady-state inflation rate. @@ -219,23 +219,23 @@ def solve_p0(p0, m0, α, g, π): return np.log(np.exp(m0) + g * np.exp(p0)) + α * π - p0 def solve_p0_bar(model, x0, π_bar): - p0_bar = fsolve(solve_p0, x0=x0, xtol=1e-20, args=(model.m0, - model.α, - model.g, + p0_bar = fsolve(solve_p0, x0=x0, xtol=1e-20, args=(model.m0, + model.α, + model.g, π_bar))[0] return p0_bar # Compute two initial price levels associated with π_l and π_u -p0_l = solve_p0_bar(model, - x0=np.log(220), +p0_l = solve_p0_bar(model, + x0=np.log(220), π_bar=π_l) -p0_u = solve_p0_bar(model, - x0=np.log(220), +p0_u = solve_p0_bar(model, + x0=np.log(220), π_bar=π_u) print(f'Associated initial p_0s are: {p0_l, p0_u}') ``` -### Verification +### Verification To start, let's write some code to verify that if the initial log price level $p_0$ takes one of the two values we just calculated, the inflation rate $\pi_t$ will be constant for all $t \geq 0$. @@ -249,7 +249,7 @@ def simulate_seq(p0, model, num_steps): π_seq, μ_seq, m_seq, p_seq = [], [], [model.m0], [p0] for t in range(num_steps): - + m_seq.append(np.log(np.exp(m_seq[t]) + g * np.exp(p_seq[t]))) p_seq.append(1/λ * p_seq[t] + (1 - 1/λ) * m_seq[t+1]) @@ -265,7 +265,7 @@ def simulate_seq(p0, model, num_steps): # Check π and μ at steady state print('π_bar == μ_bar:', π_seq[-1] == μ_seq[-1]) -# Check steady state m_{t+1} - m_t and p_{t+1} - p_t +# Check steady state m_{t+1} - m_t and p_{t+1} - p_t print('m_{t+1} - m_t:', m_seq[-1] - m_seq[-2]) print('p_{t+1} - p_t:', p_seq[-1] - p_seq[-2]) @@ -275,14 +275,14 @@ eq_g = lambda x: np.exp(-model.α * x) - np.exp(-(1 + model.α) * x) print('eq_g == g:', np.isclose(eq_g(m_seq[-1] - m_seq[-2]), model.g)) ``` -## Computing an Equilibrium Sequence +## Computing an Equilibrium Sequence -We'll deploy a method similar to *Method 2* used in {doc}`money_inflation`. +We'll deploy a method similar to *Method 2* used in {doc}`money_inflation`. We'll take the time $t$ state vector to be the pair $(m_t, p_t)$. We'll treat $m_t$ as a ``natural state variable`` and $p_t$ as a ``jump`` variable. - + Let $$ @@ -292,7 +292,7 @@ $$ Let's rewrite equation {eq}`eq:mdemand` as $$ -p_t = (1-\lambda) m_{t+1} + \lambda p_{t+1} +p_t = (1-\lambda) m_{t+1} + \lambda p_{t+1} $$ (eq:mdemand2) We'll summarize our algorithm with the following pseudo-code. @@ -317,22 +317,22 @@ Now initiate the algorithm as follows. * set $m_0 >0$ * set a value of $p_0 \in [\underline p(m_0), \overline p(m_0)]$ and form the pair $(m_0, p_0)$ at time $t =0$ - + Starting from $(m_0, p_0)$ iterate on $t$ to convergence of $\pi_t \rightarrow \overline \pi$ and $\mu_t \rightarrow \overline \mu$ - -It will turn out that + +It will turn out that * if they exist, limiting values $\overline \pi$ and $\overline \mu$ will be equal * if limiting values exist, there are two possible limiting values, one high, one low -* for almost all initial log price levels $p_0$, the limiting $\overline \pi = \overline \mu$ is +* for almost all initial log price levels $p_0$, the limiting $\overline \pi = \overline \mu$ is the higher value * for each of the two possible limiting values $\overline \pi$ ,there is a unique initial log price level $p_0$ that implies that $\pi_t = \mu_t = \overline \mu$ for all $t \geq 0$ * this unique initial log price level solves $\log(\exp(m_0) + g \exp(p_0)) - p_0 = - \alpha \overline \pi $ - + * the preceding equation for $p_0$ comes from $m_1 - p_0 = - \alpha \overline \pi$ @@ -346,10 +346,10 @@ We are now equipped to compute time series starting from different $p_0$ setti def draw_iterations(p0s, model, line_params, p0_bars, num_steps): fig, axes = plt.subplots(4, 1, figsize=(8, 10), sharex=True) - + # Pre-compute time steps - time_steps = np.arange(num_steps) - + time_steps = np.arange(num_steps) + # Plot the first two y-axes in log scale for ax in axes[:2]: ax.set_yscale('log') @@ -363,27 +363,27 @@ def draw_iterations(p0s, model, line_params, p0_bars, num_steps): # Plot p_t axes[1].plot(time_steps, p_seq[1:], **line_params) - + # Plot π_t axes[2].plot(time_steps, π_seq, **line_params) - + # Plot μ_t axes[3].plot(time_steps, μ_seq, **line_params) - + # Draw labels axes[0].set_ylabel('$m_t$') axes[1].set_ylabel('$p_t$') axes[2].set_ylabel(r'$\pi_t$') axes[3].set_ylabel(r'$\mu_t$') axes[3].set_xlabel('timestep') - + for p_0, label in [(p0_bars[0], '$p_0=p_l$'), (p0_bars[1], '$p_0=p_u$')]: y = simulate_seq(p_0, model, 1)[0] for ax in axes[2:]: ax.axhline(y=y[0], color='grey', linestyle='--', lw=1.5, alpha=0.6) - ax.text(num_steps * 1.02, y[0], label, verticalalignment='center', + ax.text(num_steps * 1.02, y[0], label, verticalalignment='center', color='grey', size=10) - + # Enforce integar axis label axes[3].xaxis.set_major_locator(MaxNLocator(integer=True)) @@ -403,37 +403,342 @@ mystnb: --- # Generate a sequence from p0_l to p0_u -p0s = np.arange(p0_l, p0_u, 0.1) +p0s = np.arange(p0_l, p0_u, 0.1) -line_params = {'lw': 1.5, +line_params = {'lw': 1.5, 'marker': 'o', 'markersize': 3} p0_bars = (p0_l, p0_u) - + draw_iterations(p0s, model, line_params, p0_bars, num_steps=20) ``` -Staring at the paths of price levels in {numref}`p0_path_nonlin` reveals that almost all paths converge to the *higher* inflation tax rate displayed in the stationary state Laffer curve. displayed in figure {numref}`laffer_curve_nonlinear`. +Staring at the paths of price levels in {numref}`p0_path_nonlin` reveals that almost all paths converge to the *higher* inflation tax rate displayed in the stationary state Laffer curve. displayed in figure {numref}`laffer_curve_nonlinear`. Thus, we have reconfirmed what we have called the "perverse" dynamics under rational expectations in which the system converges to the higher of two possible stationary inflation tax rates. Those dynamics are "perverse" not only in the sense that they imply that the monetary and fiscal authorities that have chosen to finance government expenditures eventually impose a higher inflation tax than required to finance government expenditures, but because of the following "counterintuitive" situation that we can deduce by staring at the stationary state Laffer curve displayed in figure {numref}`laffer_curve_nonlinear`: -* the figure indicates that inflation can be *reduced* by running *higher* government deficits, i.e., by raising more resources through printing money. +* the figure indicates that inflation can be *reduced* by running *higher* government deficits, i.e., by raising more resources through printing money. ```{note} The same qualitative outcomes prevail in {doc}`money_inflation` that studies a linear version of the model in this lecture. ``` -We discovered that +We discovered that * all but one of the equilibrium paths converge to limits in which the higher of two possible stationary inflation tax prevails * there is a unique equilibrium path associated with "plausible" statements about how reductions in government deficits affect a stationary inflation rate As in {doc}`money_inflation`, -on grounds of plausibility, we again recommend selecting the unique equilibrium that converges to the lower stationary inflation tax rate. +on grounds of plausibility, we again recommend selecting the unique equilibrium that converges to the lower stationary inflation tax rate. -As we shall see, we accepting this recommendation is a key ingredient of outcomes of the "unpleasant arithmetic" that we describe in {doc}`unpleasant`. +As we shall see, accepting this recommendation is a key ingredient of outcomes of the "unpleasant arithmetic" that we describe in {doc}`unpleasant`. In {doc}`laffer_adaptive`, we shall explore how {cite}`bruno1990seigniorage` and others justified our equilibrium selection in other ways. + +## Exercises + +```{exercise} +:label: mni_ex1 + +**Peak seigniorage revenue and fiscal limits.** + +The steady-state seigniorage collected at inflation rate $x$ is + +$$ +L(x) = e^{-\alpha x} - e^{-(1+\alpha)x}. +$$ + +a. Verify analytically that $L(x)$ is maximized at + +$$ +x^* = \ln\!\left(\frac{1+\alpha}{\alpha}\right) +$$ + +by differentiating, setting $L'(x) = 0$, and solving for $x$. + +b. Using the default model, compute $x^*$ from the analytic formula, confirm it + numerically with `scipy.optimize.minimize_scalar`, and plot the Laffer curve + with horizontal lines marking $g_{\rm max} = L(x^*)$ and the benchmark + deficit $g = 0.35$. + +c. Evaluate $L(x) - g$ over a fine grid of $x$ values for + $g = g_{\rm max} + 0.01$ and explain what happens if the government attempts + to finance a deficit $g > g_{\rm max}$. +``` + +```{solution-start} mni_ex1 +:class: dropdown +``` + +**Part a.** Differentiating $L(x)$: + +$$ +L'(x) = -\alpha e^{-\alpha x} + (1+\alpha)\, e^{-(1+\alpha)x} = 0. +$$ + +Rearranging: + +$$ +\frac{1+\alpha}{\alpha} = e^{(1+\alpha)x - \alpha x} = e^{x} +\implies x^* = \ln\!\left(\frac{1+\alpha}{\alpha}\right). +$$ + +Since $L''(x^*) < 0$, this is indeed a maximum. + +```{code-cell} ipython3 +from scipy.optimize import minimize_scalar + +α = model.α +x_star_analytic = np.log((1 + α) / α) +g_max = compute_seign(x_star_analytic, α) +print(f"Analytic x* = ln((1+α)/α) = {x_star_analytic:.6f}") +print(f"g_max = L(x*) = {g_max:.6f}") + +result = minimize_scalar(lambda x: -compute_seign(x, α), bounds=(0, 10), method='bounded') +print(f"Numerical x* = {result.x:.6f}") +``` + +```{code-cell} ipython3 +x_values = np.linspace(0, 5, 1000) +y_values = compute_seign(x_values, α) + +fig, ax = plt.subplots() +ax.plot(x_values, y_values, label=r'$L(x)$') +ax.axhline(g_max, color='red', linestyle='--', + label=f'$g_{{\\rm max}}={g_max:.4f}$') +ax.axhline(model.g, color='blue', linestyle='--', lw=1, + label=f'$g={model.g}$') +ax.axvline(x_star_analytic, color='grey', linestyle=':', lw=1, + label=f'$x^*={x_star_analytic:.4f}$') +ax.set_xlabel(r'steady-state inflation rate $x$') +ax.set_ylabel('seigniorage') +ax.legend() +plt.tight_layout() +plt.show() +``` + +**Part c.** + +```{code-cell} ipython3 +g_infeasible = g_max + 0.01 +x_grid = np.linspace(0, 10, 10_000) +residual = compute_seign(x_grid, α) - g_infeasible + +print(f"g_max = {g_max:.6f}") +print(f"g_infeasible = {g_infeasible:.6f}") +print(f"max L(x) - g = {residual.max():.8f} (always negative)") +``` + +When $g > g_{\rm max}$, the equation $L(x) = g$ has no real solution for any +$x \geq 0$. + +Economically, the inflation tax cannot raise enough revenue to cover the deficit at *any* inflation rate because the government has exceeded the fiscal limit of the seigniorage Laffer curve. + +No stationary equilibrium exists. + +```{solution-end} +``` + +```{exercise} +:label: mni_ex2 + +**How the two steady-state inflation rates depend on the deficit $g$.** + +The lecture computes $\pi_l$ and $\pi_u$ for a fixed benchmark deficit +$g = 0.35$. + +a. For $g$ ranging from $0.05$ to just below $g_{\rm max}$, compute both + $\pi_l(g)$ and $\pi_u(g)$ numerically and plot them on the same graph. + +b. Describe the limiting behavior: + - As $g \to 0^+$, what do $\pi_l(g)$ and $\pi_u(g)$ approach? + - As $g \to g_{\rm max}^-$, what do they approach? + +c. Mark the benchmark $g = 0.35$ on your graph, read off the two values, and + confirm they agree with `π_l` and `π_u` computed in the lecture. +``` + +```{solution-start} mni_ex2 +:class: dropdown +``` + +```{code-cell} ipython3 +α = model.α +x_star = np.log((1 + α) / α) +g_max = compute_seign(x_star, α) + +g_grid = np.linspace(0.05, g_max * 0.999, 200) +π_l_curve, π_u_curve = [], [] + +for g in g_grid: + m_temp = create_model(α=α, g=g) + π_l_curve.append(solve_π_bar(m_temp, x0=0.3)) + π_u_curve.append(solve_π_bar(m_temp, x0=3.0)) + +π_l_curve = np.array(π_l_curve) +π_u_curve = np.array(π_u_curve) + +fig, ax = plt.subplots() +ax.plot(g_grid, π_l_curve, label=r'$\pi_l(g)$ - low-inflation steady state') +ax.plot(g_grid, π_u_curve, label=r'$\pi_u(g)$ - high-inflation steady state') +ax.axvline(model.g, color='grey', linestyle='--', lw=1, + label=f'benchmark $g={model.g}$') +ax.set_xlabel('government deficit $g$') +ax.set_ylabel('steady-state inflation rate') +ax.legend() +plt.tight_layout() +plt.show() +``` + +**Part b.** + +```{code-cell} ipython3 +print(f"As g -> 0: π_l -> {π_l_curve[0]:.4f} (approaches 0)") +print(f" π_u -> {π_u_curve[0]:.4f} (large; approaches infinity)") +print(f"As g -> g_max: π_l -> {π_l_curve[-1]:.4f}") +print(f" π_u -> {π_u_curve[-1]:.4f}") +print(f"x* = {x_star:.4f} (the two roots merge here)") +``` + +When $g \to 0^+$, the curve $L(x) = g$ has one root near $x = 0$ +($\pi_l \to 0$) and another at very large $x$ ($\pi_u \to \infty$). + +As $g$ rises toward $g_{\rm max}$ the two roots approach each other and merge +at $x^* = \ln\bigl((1+\alpha)/\alpha\bigr)$. + +**Part c.** + +```{code-cell} ipython3 +idx = np.argmin(np.abs(g_grid - model.g)) +π_l_bench = solve_π_bar(model, x0=0.3) +π_u_bench = solve_π_bar(model, x0=3.0) + +print(f"At benchmark g = {model.g}:") +print(f" π_l from curve = {π_l_curve[idx]:.4f}, π_l from lecture = {π_l:.4f}") +print(f" π_u from curve = {π_u_curve[idx]:.4f}, π_u from lecture = {π_u:.4f}") +print(f" direct solve = ({π_l_bench:.4f}, {π_u_bench:.4f})") +``` + +```{solution-end} +``` + +```{exercise} +:label: mni_ex3 + +**Effect of the money-demand elasticity $\alpha$.** + +The parameter $\alpha$ governs how sensitive real money demand is to expected +inflation. + +A larger $\alpha$ means households reduce their real balances more +sharply as inflation rises. + +a. For $\alpha \in \{0.3,\; 0.5,\; 0.8\}$, compute + $x^*(\alpha) = \ln\bigl((1+\alpha)/\alpha\bigr)$ and + $g_{\rm max}(\alpha) = \alpha^\alpha/(1+\alpha)^{1+\alpha}$, then plot the + three Laffer curves on the same axes. + +b. Keeping the deficit fixed at $g = 0.35$, check for each $\alpha$ + whether the deficit is feasible (i.e., $g \leq g_{\rm max}(\alpha)$) and + compute $(\pi_l,\, \pi_u)$ for each feasible case. + +c. For every feasible $\alpha$, simulate 20 periods starting from the + midpoint $p_0 = \tfrac{1}{2}(p_{0,l} + p_{0,u})$ and plot $\pi_t$ on a + single graph to identify what the paths have in common. +``` + +```{solution-start} mni_ex3 +:class: dropdown +``` + +**Part a.** + +```{code-cell} ipython3 +alphas = [0.3, 0.5, 0.8] +x_values = np.linspace(0, 6, 1000) + +fig, ax = plt.subplots() +for α in alphas: + y_values = compute_seign(x_values, α) + x_star_α = np.log((1 + α) / α) + g_max_α = compute_seign(x_star_α, α) + ax.plot(x_values, y_values, + label=fr'$\alpha={α}$, $g_{{\rm max}}={g_max_α:.3f}$') + print(f"α = {α}: x* = {x_star_α:.4f}, g_max = {g_max_α:.4f}") + +ax.axhline(0.35, color='red', linestyle='--', lw=1, label='$g=0.35$') +ax.set_xlabel(r'steady-state inflation rate $x$') +ax.set_ylabel('seigniorage') +ax.set_ylim([0, 0.6]) +ax.legend() +plt.tight_layout() +plt.show() +``` + +A larger $\alpha$ flattens and narrows the Laffer curve, reducing the maximum +seigniorage the government can raise. + +**Part b.** + +```{code-cell} ipython3 +g = 0.35 +print(f"Feasibility check for g = {g}\n") + +steady_states = {} +for α in alphas: + m_temp = create_model(α=α, g=g) + x_star_α = np.log((1 + α) / α) + g_max_α = compute_seign(x_star_α, α) + if g <= g_max_α: + pl = solve_π_bar(m_temp, x0=0.3) + pu = solve_π_bar(m_temp, x0=3.0) + steady_states[α] = (pl, pu) + print(f"α = {α}: feasible (g_max = {g_max_α:.4f})" + f" -> π_l = {pl:.4f}, π_u = {pu:.4f}") + else: + steady_states[α] = None + print(f"α = {α}: infeasible (g_max = {g_max_α:.4f} < g = {g})") +``` + +For $\alpha = 0.8$ the maximum seigniorage falls below $g = 0.35$, so no +steady-state equilibrium exists. + +Higher money-demand sensitivity tightens the fiscal limit of the inflation tax. + +**Part c.** + +```{code-cell} ipython3 +num_steps = 20 +fig, ax = plt.subplots() + +for α in alphas: + if steady_states[α] is None: + continue + π_l_α, π_u_α = steady_states[α] + m_temp = create_model(α=α, g=g) + p0_l_α = solve_p0_bar(m_temp, x0=np.log(220), π_bar=π_l_α) + p0_u_α = solve_p0_bar(m_temp, x0=np.log(220), π_bar=π_u_α) + p0_mid = (p0_l_α + p0_u_α) / 2 + + π_seq_α, *_ = simulate_seq(p0_mid, m_temp, num_steps) + ax.plot(range(num_steps), π_seq_α, + marker='o', markersize=3, lw=1.5, + label=fr'$\alpha={α}$ ($\pi_u={π_u_α:.2f}$)') + ax.axhline(π_u_α, linestyle='--', lw=1, alpha=0.4) + +ax.set_xlabel('time step') +ax.set_ylabel(r'$\pi_t$') +ax.legend() +plt.tight_layout() +plt.show() +``` + +For every feasible $\alpha$, the path from the midpoint $p_0$ converges to the +*high*-inflation steady state $\pi_u$, confirming the perverse dynamics under +rational expectations regardless of the value of $\alpha$. + +```{solution-end} +``` diff --git a/lectures/networks.md b/lectures/networks.md index e2072046e..d2bb45507 100644 --- a/lectures/networks.md +++ b/lectures/networks.md @@ -1022,7 +1022,7 @@ directed graph with adjacency matrix $A$ is defined as the vector $\kappa$ that solves $$ -\kappa_i = \beta \sum_{1 \leq j 1} a_{ij} \kappa_j + 1 +\kappa_i = \beta \sum_{j=0}^{n-1} a_{ij} \kappa_j + 1 \qquad \text{for all } i \in \{0, \ldots, n-1\}. $$ (katz_central) diff --git a/lectures/pv.md b/lectures/pv.md index 1a10dbc21..02ca22076 100644 --- a/lectures/pv.md +++ b/lectures/pv.md @@ -13,7 +13,7 @@ kernelspec: # Present Values -## Overview +## Overview This lecture describes the **present value model** that is a starting point of much asset pricing theory. @@ -22,7 +22,7 @@ Asset pricing theory is a component of theories about many economic decisions in * consumption * labor supply - * education choice + * education choice * demand for money In asset pricing theory, and in economic dynamics more generally, a basic topic is the relationship @@ -34,7 +34,7 @@ In this lecture, we'll represent a sequence as a vector. So our analysis will typically boil down to studying relationships among vectors. -Our main tools in this lecture will be +Our main tools in this lecture will be * matrix multiplication, and * matrix inversion. @@ -44,19 +44,19 @@ We'll use the calculations described here in subsequent lectures, including {do Let's dive in. -## Analysis +## Analysis -Let +Let * $\{d_t\}_{t=0}^T $ be a sequence of dividends or "payouts" * $\{p_t\}_{t=0}^T $ be a sequence of prices of a claim on the continuation of - the asset's payout stream from date $t$ on, namely, $\{d_s\}_{s=t}^T $ - * $ \delta \in (0,1) $ be a one-period "discount factor" + the asset's payout stream from date $t$ on, namely, $\{d_s\}_{s=t}^T $ + * $ \delta \in (0,1) $ be a one-period "discount factor" * $p_{T+1}^*$ be a terminal price of the asset at time $T+1$ - -We assume that the dividend stream $\{d_t\}_{t=0}^T $ and the terminal price + +We assume that the dividend stream $\{d_t\}_{t=0}^T $ and the terminal price $p_{T+1}^*$ are both exogenous. This means that they are determined outside the model. @@ -80,7 +80,7 @@ price $p_{T+1}^*$. A system of equations like {eq}`eq:Euler1` is an example of a linear **difference equation**. There are powerful mathematical methods available for solving such systems and they are well worth -studying in their own right, being the foundation for the analysis of many interesting economic models. +studying in their own right, being the foundation for the analysis of many interesting economic models. For an example, see {doc}`Samuelson multiplier-accelerator ` @@ -120,19 +120,19 @@ $$ \vdots & \vdots & \vdots & \vdots & \vdots & 0 & 0 \cr 0 & 0 & 0 & 0 & \cdots & 1 & -\delta \cr 0 & 0 & 0 & 0 & \cdots & 0 & 1 \end{bmatrix} - \begin{bmatrix} p_0 \cr p_1 \cr p_2 \cr \vdots \cr p_{T-1} \cr p_T - \end{bmatrix} - = \begin{bmatrix} + \begin{bmatrix} p_0 \cr p_1 \cr p_2 \cr \vdots \cr p_{T-1} \cr p_T + \end{bmatrix} + = \begin{bmatrix} d_0 \cr d_1 \cr d_2 \cr \vdots \cr d_{T-1} \cr d_T \end{bmatrix} - + \begin{bmatrix} + + \begin{bmatrix} 0 \cr 0 \cr 0 \cr \vdots \cr 0 \cr \delta p_{T+1}^* \end{bmatrix} $$ (eq:pvpieq) +++ -```{exercise-start} +```{exercise-start} :label: pv_ex_1 ``` @@ -142,7 +142,47 @@ recover the equations in [](eq:Euler_stack). ```{exercise-end} ``` -In vector-matrix notation, we can write system {eq}`eq:pvpieq` as +```{solution-start} pv_ex_1 +:class: dropdown +``` + +Multiplying row $t$ of the matrix (which has $1$ in column $t$ and $-\delta$ in +column $t+1$) against the price vector gives $p_t - \delta p_{t+1}$. + +The last row has only a $1$ in column $T$, giving $p_T$. + +Setting these equal to the right-hand side recovers exactly the equations in {eq}`eq:Euler_stack`. + +We can verify the result numerically. + +```{code-cell} ipython3 +T = 6 +δ = 0.99 +p_star = 10.0 +d = np.array([1.0 * 1.05**t for t in range(T+1)]) + +# Build A +A = np.zeros((T+1, T+1)) +for i in range(T+1): + A[i, i] = 1 + if i < T: + A[i, i+1] = -δ + +b = np.zeros(T+1) +b[-1] = δ * p_star + +# Solve for p +p = np.linalg.solve(A, d + b) + +# Check that A @ p == d + b (residual should be zero) +residual = A @ p - (d + b) +print("Max residual |A p - (d + b)|:", np.max(np.abs(residual))) +``` + +```{solution-end} +``` + +In vector-matrix notation, we can write system {eq}`eq:pvpieq` as $$ A p = d + b @@ -151,7 +191,7 @@ $$ (eq:apdb) Here $A$ is the matrix on the left side of equation {eq}`eq:pvpieq`, while $$ - p = + p = \begin{bmatrix} p_0 \\ p_1 \\ @@ -159,7 +199,7 @@ $$ p_T \end{bmatrix}, \quad - d = + d = \begin{bmatrix} d_0 \\ d_1 \\ @@ -167,7 +207,7 @@ $$ d_T \end{bmatrix}, \quad \text{and} \quad - b = + b = \begin{bmatrix} 0 \\ 0 \\ @@ -176,14 +216,14 @@ $$ \end{bmatrix} $$ -The solution for the vector of prices is +The solution for the vector of prices is $$ p = A^{-1}(d + b) $$ (eq:apdb_sol) -For example, suppose that the dividend stream is +For example, suppose that the dividend stream is $$ d_{t+1} = 1.05 d_t, \quad t = 0, 1, \ldots , T-1. @@ -197,7 +237,7 @@ current_d = 1.0 d = [] for t in range(T+1): d.append(current_d) - current_d = current_d * 1.05 + current_d = current_d * 1.05 fig, ax = plt.subplots() ax.plot(d, 'o', label='dividends') @@ -269,7 +309,7 @@ ax.set_xlabel('time') plt.show() ``` -```{exercise-start} +```{exercise-start} :label: pv_ex_cyc ``` @@ -311,7 +351,7 @@ The weighted averaging associated with the present value calculation largely eliminates the cycles. -```{solution-end} +```{solution-end} ``` ## Analytical expressions @@ -321,7 +361,7 @@ By the [inverse matrix theorem](https://en.wikipedia.org/wiki/Invertible_matrix) It can be verified that the inverse of the matrix $A$ in {eq}`eq:pvpieq` is -$$ A^{-1} = +$$ A^{-1} = \begin{bmatrix} 1 & \delta & \delta^2 & \cdots & \delta^{T-1} & \delta^T \cr 0 & 1 & \delta & \cdots & \delta^{T-2} & \delta^{T-1} \cr @@ -333,7 +373,7 @@ $$ (eq:Ainv) -```{exercise-start} +```{exercise-start} :label: pv_ex_2 ``` @@ -342,6 +382,35 @@ Check this by showing that $A A^{-1}$ is equal to the identity matrix. ```{exercise-end} ``` +```{solution-start} pv_ex_2 +:class: dropdown +``` + +```{code-cell} ipython3 +T = 6 +δ = 0.99 + +# Build A +A = np.zeros((T+1, T+1)) +for i in range(T+1): + A[i, i] = 1 + if i < T: + A[i, i+1] = -δ + +# Analytical inverse from eq:Ainv: A_inv[i,j] = δ^(j-i) for j >= i, else 0 +A_inv = np.zeros((T+1, T+1)) +for i in range(T+1): + for j in range(i, T+1): + A_inv[i, j] = δ**(j - i) + +# Verify +print("A @ A_inv (should be identity):") +print(np.round(A @ A_inv, 10)) +print("Is identity:", np.allclose(A @ A_inv, np.eye(T+1))) +``` + +```{solution-end} +``` If we use the expression {eq}`eq:Ainv` in {eq}`eq:apdb_sol` and perform the indicated matrix multiplication, we shall find that @@ -349,13 +418,13 @@ $$ p_t = \sum_{s=t}^T \delta^{s-t} d_s + \delta^{T+1-t} p_{T+1}^* $$ (eq:ptpveq) -Pricing formula {eq}`eq:ptpveq` asserts that two components sum to the asset price +Pricing formula {eq}`eq:ptpveq` asserts that two components sum to the asset price $p_t$: * a **fundamental component** $\sum_{s=t}^T \delta^{s-t} d_s$ that equals the **discounted present value** of prospective dividends - + * a **bubble component** $\delta^{T+1-t} p_{T+1}^*$ - + The fundamental component is pinned down by the discount factor $\delta$ and the payout of the asset (in this case, dividends). @@ -364,13 +433,13 @@ fundamentals. It is sometimes convenient to rewrite the bubble component as -$$ +$$ c \delta^{-t} $$ -where +where -$$ +$$ c \equiv \delta^{T+1}p_{T+1}^* $$ @@ -381,10 +450,10 @@ $$ For a few moments, let's focus on the special case of an asset that never pays dividends, in which case $$ -\begin{bmatrix} +\begin{bmatrix} d_0 \cr d_1 \cr d_2 \cr \vdots \cr d_{T-1} \cr d_T -\end{bmatrix} = -\begin{bmatrix} +\end{bmatrix} = +\begin{bmatrix} 0 \cr 0 \cr 0 \cr \vdots \cr 0 \cr 0 \end{bmatrix} $$ @@ -401,27 +470,27 @@ $$ \vdots & \vdots & \vdots & \vdots & \vdots & 0 & 0 \cr 0 & 0 & 0 & 0 & \cdots & 1 & -\delta \cr 0 & 0 & 0 & 0 & \cdots & 0 & 1 \end{bmatrix} -\begin{bmatrix} p_0 \cr p_1 \cr p_2 \cr \vdots \cr p_{T-1} \cr p_T +\begin{bmatrix} p_0 \cr p_1 \cr p_2 \cr \vdots \cr p_{T-1} \cr p_T \end{bmatrix} = -\begin{bmatrix} +\begin{bmatrix} 0 \cr 0 \cr 0 \cr \vdots \cr 0 \cr \delta p_{T+1}^* \end{bmatrix} $$ (eq:pieq2) Evidently, if $p_{T+1}^* = 0$, a price vector $p$ of all entries zero -solves this equation and the only the **fundamental** component of our pricing -formula {eq}`eq:ptpveq` is present. +solves this equation and the only the **fundamental** component of our pricing +formula {eq}`eq:ptpveq` is present. -But let's activate the **bubble** component by setting +But let's activate the **bubble** component by setting $$ -p_{T+1}^* = c \delta^{-(T+1)} +p_{T+1}^* = c \delta^{-(T+1)} $$ (eq:eqbubbleterm) for some positive constant $c$. In this case, when we multiply both sides of {eq}`eq:pieq2` by -the matrix $A^{-1}$ presented in equation {eq}`eq:Ainv`, we +the matrix $A^{-1}$ presented in equation {eq}`eq:Ainv`, we find that $$ @@ -432,7 +501,7 @@ $$ (eq:bubble) ## Gross rate of return Define the gross rate of return on holding the asset from period $t$ to period $t+1$ -as +as $$ R_t = \frac{p_{t+1}}{p_t} @@ -448,11 +517,11 @@ $$ ## Exercises -```{exercise-start} +```{exercise-start} :label: pv_ex_a ``` -Assume that $g >1$ and that $\delta g \in (0,1)$. Give analytical expressions for an asset price $p_t$ under the +Assume that $g >1$ and that $\delta g \in (0,1)$. Give analytical expressions for an asset price $p_t$ under the following settings for $d$ and $p_{T+1}^*$: 1. $p_{T+1}^* = 0, d_t = g^t d_0$ (a modified version of the Gordon growth formula) @@ -461,7 +530,7 @@ following settings for $d$ and $p_{T+1}^*$: 1. $p_{T+1}^* = c \delta^{-(T+1)}, d_t = 0$ (price of a pure bubble stock) -```{exercise-end} +```{exercise-end} ``` ```{solution-start} pv_ex_a @@ -477,4 +546,160 @@ Plugging each of the above $p_{T+1}^*, d_t$ pairs into Equation {eq}`eq:ptpveq` ```{solution-end} -``` \ No newline at end of file +``` + +```{exercise} +:label: pv_ex_b + +Verify pricing formula {eq}`eq:ptpveq` numerically for the growing dividend example +in the lecture ($d_{t+1} = 1.05 d_t$, $d_0 = 1$, $T = 6$, $\delta = 0.99$, +$p_{T+1}^* = 10$). + +For each $t = 0, 1, \ldots, T$, compute $p_t$ both + +1. by solving the linear system $Ap = d + b$ (as in the lecture), and +2. by directly evaluating the sum $\sum_{s=t}^T \delta^{s-t} d_s + \delta^{T+1-t} p_{T+1}^*$. + +Print both results side by side and confirm they match. +``` + +```{solution-start} pv_ex_b +:class: dropdown +``` + +```{code-cell} ipython3 +T = 6 +δ = 0.99 +p_star = 10.0 +d = np.array([1.0 * 1.05**t for t in range(T+1)]) + +A = np.zeros((T+1, T+1)) +for i in range(T+1): + A[i, i] = 1 + if i < T: + A[i, i+1] = -δ +b = np.zeros(T+1) +b[-1] = δ * p_star +p_matrix = np.linalg.solve(A, d + b) + +p_formula = np.array([ + sum(δ**(s-t) * d[s] for s in range(t, T+1)) + δ**(T+1-t) * p_star + for t in range(T+1) +]) + +print(f"{'t':>3} | {'matrix':>12} | {'formula':>12} | {'|diff|':>10}") +print('-' * 44) +for t in range(T+1): + diff = abs(p_matrix[t] - p_formula[t]) + print(f'{t:>3} | {p_matrix[t]:>12.6f} | {p_formula[t]:>12.6f} | {diff:>10.2e}') +``` + +```{solution-end} +``` + +```{exercise} +:label: pv_ex_c + +Suppose dividends are constant: $d_t = d = 1$ for all $t = 0, \ldots, T$. + +Set the terminal price to the perpetuity value $p_{T+1}^* = d / (1-\delta)$. + +a. Compute the asset price sequence for $T = 100$ and $\delta = 0.99$ and plot + $p_t$ alongside the perpetuity value $d/(1-\delta)$ as a dashed line. + +b. Verify analytically (using formula {eq}`eq:ptpveq`) that + $p_t = d / (1-\delta)$ for all $t$. +``` + +```{solution-start} pv_ex_c +:class: dropdown +``` + +```{code-cell} ipython3 +T = 100 +δ = 0.99 +d_const = 1.0 +p_star_perp = d_const / (1 - δ) + +d = d_const * np.ones(T+1) +A = np.zeros((T+1, T+1)) +for i in range(T+1): + A[i, i] = 1 + if i < T: + A[i, i+1] = -δ + +b = np.zeros(T+1) +b[-1] = δ * p_star_perp + +p = np.linalg.solve(A, d + b) + +fig, ax = plt.subplots() +ax.plot(p, 'o-', ms=3, label='Asset price $p_t$') +ax.axhline(p_star_perp, linestyle='--', color='red', + label=f'Perpetuity value $d/(1 - δ) = {p_star_perp:.2f}$') +ax.set_xlabel('time') +ax.set_title('Constant dividend: asset price equals perpetuity value') +ax.legend() +plt.show() + +print(f'Max deviation from d/(1 - δ): {np.max(np.abs(p - p_star_perp)):.2e}') +``` + +For part b, substituting $d_s = d$ and $p_{T+1}^* = d/(1-\delta)$ into {eq}`eq:ptpveq` gives + +$$ +p_t = d \frac{1 - \delta^{T+1-t}}{1-\delta} + \frac{d\delta^{T+1-t}}{1-\delta} + = \frac{d}{1-\delta} +$$ + +```{solution-end} +``` + +```{exercise} +:label: pv_ex_d + +For the growing dividend stream ($d_{t+1} = 1.05 d_t$, $d_0 = 1$, $T = 6$, +$p_{T+1}^* = 10$), plot the asset price at $t = 0$ as a function of the +discount factor $\delta \in [0.90,\, 0.99]$. + +Verify that $p_0$ is strictly increasing in $\delta$ and explain why in terms +of the formula {eq}`eq:ptpveq`. +``` + +```{solution-start} pv_ex_d +:class: dropdown +``` + +```{code-cell} ipython3 +T = 6 +p_star = 10.0 +d = np.array([1.0 * 1.05**t for t in range(T+1)]) + +δ_vals = np.linspace(0.90, 0.99, 200) +p0_vals = [] + +for δ in δ_vals: + A = np.zeros((T+1, T+1)) + for i in range(T+1): + A[i, i] = 1 + if i < T: + A[i, i+1] = -δ + b = np.zeros(T+1) + b[-1] = δ * p_star + p = np.linalg.solve(A, d + b) + p0_vals.append(p[0]) + +fig, ax = plt.subplots() +ax.plot(δ_vals, p0_vals) +ax.set_xlabel(r'$\delta$') +ax.set_ylabel(r'$p_0$') +ax.set_title('Asset price at $t=0$ as a function of $\\delta$') +plt.show() +``` + +Each term $\delta^{s-t} d_s$ in the fundamental component and the bubble term $\delta^{T+1-t} p_{T+1}^*$ are both increasing in $\delta$. + +A higher discount factor therefore raises the present value of every future cash flow, pushing up $p_0$. + +```{solution-end} +``` diff --git a/lectures/schelling.md b/lectures/schelling.md index 8032e1050..4c1ee939e 100644 --- a/lectures/schelling.md +++ b/lectures/schelling.md @@ -231,7 +231,7 @@ def plot_distribution(agents, cycle_num): "Plot the distribution of agents after cycle_num rounds of the loop." x_values_0, y_values_0 = [], [] x_values_1, y_values_1 = [], [] - # == Obtain locations of each type == # + # Obtain locations of each type for agent in agents: x, y = agent.location if agent.type == 0: diff --git a/lectures/solow.md b/lectures/solow.md index ad6655eeb..80aa2924d 100644 --- a/lectures/solow.md +++ b/lectures/solow.md @@ -57,10 +57,10 @@ $$ Production functions with this property include * the **Cobb-Douglas** function $F(K, L) = A K^{\alpha} - L^{1-\alpha}$ with $0 \leq \alpha \leq 1$. + L^{1-\alpha}$ with $0 \leq \alpha \leq 1$. * the **CES** function $F(K, L) = \left\{ a K^\rho + b L^\rho \right\}^{1/\rho}$ - with $a, b, \rho > 0$. - + with $a, b, \rho > 0$. + Here, $\alpha$ is the output elasticity of capital and $\rho$ is a parameter that determines the elasticity of substitution between capital and labor. We assume a closed economy, so aggregate domestic investment equals aggregate domestic diff --git a/lectures/supply_demand_heterogeneity.md b/lectures/supply_demand_heterogeneity.md index 2672cda12..f862de322 100644 --- a/lectures/supply_demand_heterogeneity.md +++ b/lectures/supply_demand_heterogeneity.md @@ -124,9 +124,9 @@ $$ \mu_{i}=\frac{-W_{i}+p^{\top}\left(\Pi^{-1}b_{i}-e_{i}\right)}{p^{\top}(\Pi^{\top}\Pi)^{-1}p}$$ - Finally we compute a competitive equilibrium allocation by using the demand curves: - + $$ -c_{i}=\Pi^{-1}b_{i}-(\Pi^{\top}\Pi)^{-1}\mu_{i}p +c_{i}=\Pi^{-1}b_{i}-(\Pi^{\top}\Pi)^{-1}\mu_{i}p $$ @@ -167,11 +167,11 @@ Now let's proceed to code. ```{code-cell} ipython3 class ExchangeEconomy: - def __init__(self, - Π, - bs, - es, - Ws=None, + def __init__(self, + Π, + bs, + es, + Ws=None, thres=1.5): """ Set up the environment for an exchange economy @@ -236,7 +236,7 @@ class ExchangeEconomy: ## Implementation -Next we use the class ``ExchangeEconomy`` defined above to study +Next we use the class ``ExchangeEconomy`` defined above to study * a two-person economy without production, * a dynamic economy, and @@ -376,20 +376,20 @@ We have the following objects - The demand curve: - -$$ -c_{i}=\Pi^{-1}b_{i}-(\Pi^{\top}\Pi)^{-1}\mu_{i}p + +$$ +c_{i}=\Pi^{-1}b_{i}-(\Pi^{\top}\Pi)^{-1}\mu_{i}p $$ - The marginal utility of wealth: - -$$ + +$$ \mu_{i}=\frac{-W_{i}+p^{\top}\left(\Pi^{-1}b_{i}-e_{i}\right)}{p^{\top}(\Pi^{\top}\Pi)^{-1}p} $$ - Market clearing: - -$$ + +$$ \sum c_{i}=\sum e_{i} $$ @@ -397,7 +397,7 @@ Denote aggregate consumption $\sum_i c_{i}=c$ and $\sum_i \mu_i = \mu$. Market clearing requires -$$ +$$ \Pi^{-1}\left(\sum_{i}b_{i}\right)-(\Pi^{\top}\Pi)^{-1}p\left(\sum_{i}\mu_{i}\right)=\sum_{i}e_{i} $$ which, after a few steps, leads to @@ -408,7 +408,7 @@ $$ where -$$ +$$ \mu = \sum_i\mu_{i}=\frac{0 + p^{\top}\left(\Pi^{-1}b-e\right)}{p^{\top}(\Pi^{\top}\Pi)^{-1}p}. $$ @@ -434,5 +434,5 @@ $$ p=\tilde{\mu}^{-1}(\Pi^{\top}b-\Pi^{\top}\Pi e) $$ -Thus, we have verified that, up to the choice of a numeraire in which to express absolute prices, the price +Thus, we have verified that, up to the choice of a numeraire in which to express absolute prices, the price vector in our representative consumer economy is the same as that in an underlying economy with multiple consumers. diff --git a/lectures/tax_smooth.md b/lectures/tax_smooth.md index 128541de3..0dd2a3038 100644 --- a/lectures/tax_smooth.md +++ b/lectures/tax_smooth.md @@ -30,7 +30,7 @@ The government minimizes those costs by smoothing tax collections over time an The present value of government expenditures is at the core of the tax-smoothing model, so we'll again use formulas presented in {doc}`present value formulas`. -We'll again use the matrix multiplication and matrix inversion tools that we used in {doc}`present value formulas `. +We'll again use the matrix multiplication and matrix inversion tools that we used in {doc}`present value formulas `. @@ -59,14 +59,14 @@ The government can borrow or lend at interest rate $R$, subject to some limits o Let - * $S \geq 2$ be a positive integer that constitutes a time-horizon. - * $G = \{G_t\}_{t=0}^S$ be a sequence of government expenditures. - * $B = \{B_t\}_{t=0}^{S+1}$ be a sequence of government debt. - * $T = \{T_t\}_{t=0}^S$ be a sequence of tax collections. - * $R \geq 1$ be a fixed gross one period interest rate. - * $\beta \in (0,1)$ be a fixed discount factor. + * $S \geq 2$ be a positive integer that constitutes a time-horizon. + * $G = \{G_t\}_{t=0}^S$ be a sequence of government expenditures. + * $B = \{B_t\}_{t=0}^{S+1}$ be a sequence of government debt. + * $T = \{T_t\}_{t=0}^S$ be a sequence of tax collections. + * $R \geq 1$ be a fixed gross one period interest rate. + * $\beta \in (0,1)$ be a fixed discount factor. * $B_0$ be a given initial level of government debt - * $B_{S+1} \geq 0$ be a terminal condition. + * $B_{S+1} \geq 0$ be a terminal condition. The sequence of government debt $B$ is to be determined by the model. @@ -90,17 +90,17 @@ Given a sequence $G$ of government expenditures, a large set of pairs $(B, T)$ o The model follows the following logical flow: - * start with an exogenous government expenditure sequence $G$, an initial government debt $B_0$, and + * start with an exogenous government expenditure sequence $G$, an initial government debt $B_0$, and a candidate tax collection path $T$. - + * use the system of equations {eq}`eq:B_t` for $t=0, \ldots, S$ to compute a path $B$ of government debt - - * verify that $B_{S+1}$ satisfies the terminal debt constraint $B_{S+1} \geq 0$. - - * If it does, declare that the candidate path is **budget feasible**. - + + * verify that $B_{S+1}$ satisfies the terminal debt constraint $B_{S+1} \geq 0$. + + * If it does, declare that the candidate path is **budget feasible**. + * if the candidate tax path is not budget feasible, propose a different tax path and start over - + Below, we'll describe how to execute these steps using linear algebra -- matrix inversion and multiplication. The above procedure seems like a sensible way to find "budget-feasible" tax paths $T$, i.e., paths that are consistent with the exogenous government expenditure stream $G$, the initial debt level $B_0$, and the terminal debt level $B_{S+1}$. @@ -117,18 +117,18 @@ To answer this question, we assess alternative budget feasible tax paths $T$ us L = - \sum_{t=0}^S \beta^t (g_1 T_t - \frac{g_2}{2} T_t^2 ) ``` -where $g_1 > 0, g_2 > 0$. +where $g_1 > 0, g_2 > 0$. This is called the "present value of revenue-raising costs" in {cite}`Barro1979`. -The quadratic term $-\frac{g_2}{2} T_t^2$ captures increasing marginal costs of taxation, implying that tax distortions rise more than proportionally with tax rates. +The quadratic term $-\frac{g_2}{2} T_t^2$ captures increasing marginal costs of taxation, implying that tax distortions rise more than proportionally with tax rates. This creates an incentive for tax smoothing. Indeed, we shall see that when $\beta R = 1$, criterion {eq}`cost` leads to smoother tax paths. -By **smoother** we mean tax rates that are as close as possible to being constant over time. +By **smoother** we mean tax rates that are as close as possible to being constant over time. The preference for smooth tax paths that is built into the model gives it the name "tax-smoothing model". @@ -140,14 +140,14 @@ Or equivalently, we can transform this into the same problem as in the {doc}`con W = \sum_{t=0}^S \beta^t (g_1 T_t - \frac{g_2}{2} T_t^2 ) ``` -Let's dive in and do some calculations that will help us understand how the model works. +Let's dive in and do some calculations that will help us understand how the model works. -Here we use default parameters $R = 1.05$, $g_1 = 1$, $g_2 = 1/2$, and $S = 65$. +Here we use default parameters $R = 1.05$, $g_1 = 1$, $g_2 = 1/2$, and $S = 65$. We create a Python ``namedtuple`` to store these parameters with default values. ```{code-cell} ipython3 -TaxSmoothing = namedtuple("TaxSmoothing", +TaxSmoothing = namedtuple("TaxSmoothing", ["R", "g1", "g2", "β_seq", "S"]) def create_tax_smoothing_model(R=1.01, g1=1, g2=1/2, S=65): @@ -175,7 +175,7 @@ Formally it resembles the present value calculations we saw in this QuantEcon le This present value calculation is crucial for determining the government's total financing needs. -By iterating on equation {eq}`eq:B_t` and imposing the terminal condition +By iterating on equation {eq}`eq:B_t` and imposing the terminal condition $$ B_{S+1} = 0, @@ -183,15 +183,15 @@ $$ it is possible to convert a sequence of budget constraints {eq}`eq:B_t` into a single intertemporal constraint -$$ -\sum_{t=0}^S R^{-t} T_t = B_0 + h_0. +$$ +\sum_{t=0}^S R^{-t} T_t = B_0 + h_0. $$ (eq:budget_intertemp_tax) Equation {eq}`eq:budget_intertemp_tax` says that the present value of tax collections must equal the sum of initial debt and the present value of government expenditures. -When $\beta R = 1$, it is optimal for a government to smooth taxes by setting +When $\beta R = 1$, it is optimal for a government to smooth taxes by setting -$$ +$$ T_t = T_0 \quad t =0, 1, \ldots, S $$ @@ -206,7 +206,7 @@ $$ (eq:taxsmoothing) Equation {eq}`eq:taxsmoothing` is the tax-smoothing model in a nutshell. -## Mechanics of tax-smoothing +## Mechanics of tax-smoothing As promised, we'll provide step-by-step instructions on how to use linear algebra, readily implemented in Python, to compute all objects in play in the tax-smoothing model. @@ -236,17 +236,17 @@ Use the system of equations {eq}`eq:B_t` for $t=0, \ldots, S$ to compute a path To do this, we transform that system of difference equations into a single matrix equation as follows: $$ -\begin{bmatrix} +\begin{bmatrix} 1 & 0 & 0 & \cdots & 0 & 0 & 0 \cr -R & 1 & 0 & \cdots & 0 & 0 & 0 \cr 0 & -R & 1 & \cdots & 0 & 0 & 0 \cr \vdots &\vdots & \vdots & \cdots & \vdots & \vdots & \vdots \cr 0 & 0 & 0 & \cdots & -R & 1 & 0 \cr 0 & 0 & 0 & \cdots & 0 & -R & 1 -\end{bmatrix} -\begin{bmatrix} B_1 \cr B_2 \cr B_3 \cr \vdots \cr B_S \cr B_{S+1} \end{bmatrix} -= R +\begin{bmatrix} B_1 \cr B_2 \cr B_3 \cr \vdots \cr B_S \cr B_{S+1} +\end{bmatrix} += R \begin{bmatrix} G_0 + B_0 - T_0 \cr G_1 - T_0 \cr G_2 - T_0 \cr \vdots\cr G_{S-1} - T_0 \cr G_S - T_0 \end{bmatrix} $$ @@ -258,12 +258,12 @@ $$ $$ Because we have built into our calculations that the government must satisfy its intertemporal budget constraint and end with zero debt, just barely satisfying the -terminal condition that $B_{S+1} \geq 0$, it should turn out that +terminal condition that $B_{S+1} \geq 0$, it should turn out that $$ B_{S+1} = 0. $$ - + Let's verify this with Python code. First we implement the model with `compute_optimal` @@ -306,7 +306,7 @@ G_seq = np.concatenate([np.ones(46), 4*np.ones(5), np.ones(15)]) tax_model = create_tax_smoothing_model() T_seq, B_seq, h0 = compute_optimal(tax_model, B0, G_seq) -print('check B_S+1=0:', +print('check B_S+1=0:', np.abs(B_seq[-1] - 0) <= 1e-8) ``` @@ -359,31 +359,31 @@ First we create a function `plot_ts` that generates graphs for different instanc This will help us avoid rewriting code to plot outcomes for different government expenditure sequences. ```{code-cell} ipython3 -def plot_ts(model, # tax-smoothing model +def plot_ts(model, # tax-smoothing model B0, # initial government debt G_seq # government expenditure process ): - + # Compute optimal tax path T_seq, B_seq, h0 = compute_optimal(model, B0, G_seq) - + # Sequence length S = tax_model.S - + fig, axes = plt.subplots(1, 2, figsize=(12,5)) - + axes[0].plot(range(S+1), G_seq, label='expenditures', lw=2) axes[0].plot(range(S+1), T_seq, label='taxes', lw=2) axes[1].plot(range(S+2), B_seq, label='debt', color='green', lw=2) axes[0].set_ylabel(r'$T_t,G_t$') axes[1].set_ylabel(r'$B_t$') - + for ax in axes: ax.plot(range(S+2), np.zeros(S+2), '--', lw=1, color='black') ax.legend() ax.set_xlabel(r'$t$') - - + + plt.show() ``` @@ -391,21 +391,21 @@ In the experiments below, please study how tax and government debt sequences var #### Experiment 1: one-time spending shock -We first assume a one-time spending shock of $W_0$ in year 21 of the expenditure sequence $G$. +We first assume a one-time spending shock of $W_0$ in year 21 of the expenditure sequence $G$. We'll make $W_0$ big - positive to indicate a spending surge (like a war or disaster), and negative to indicate a spending cut. ```{code-cell} ipython3 # Spending surge W_0 = 2.5 -G_seq_pos = np.concatenate([np.ones(21), np.array([2.5]), +G_seq_pos = np.concatenate([np.ones(21), np.array([2.5]), np.ones(24), np.ones(20)]) plot_ts(tax_model, B0, G_seq_pos) ``` -#### Experiment 2: permanent expenditure shift +#### Experiment 2: temporary expenditure shift -Now we assume a permanent increase in government expenditures of $L$ in year 21 of the $G$-sequence. +Now we assume a temporary increase in government expenditures of $L$ beginning in year 21 of the $G$-sequence. Again we can study positive and negative cases @@ -450,7 +450,7 @@ G_0 = 1 t_max = 46 # Generate geometric G sequence -geo_seq = λ ** np.arange(t_max) * G_0 +geo_seq = λ ** np.arange(t_max) * G_0 G_seq_geo = np.concatenate( [geo_seq, np.max(geo_seq)*np.ones(20)]) @@ -461,7 +461,7 @@ Now we show the behavior when $\lambda = 0.95$ (declining expenditures) ```{code-cell} ipython3 λ = 0.95 -geo_seq = λ ** np.arange(t_max) * G_0 +geo_seq = λ ** np.arange(t_max) * G_0 G_seq_geo = np.concatenate( [geo_seq, λ ** t_max * np.ones(20)]) @@ -481,13 +481,13 @@ plot_ts(tax_model, B0, G_seq_geo) ### Feasible Tax Variations -We promised to justify our claim that a constant tax rate $T_t = T_0$ for all $t$ is optimal. +We promised to justify our claim that a constant tax rate $T_t = T_0$ for all $t$ is optimal. Let's do that now. -The approach we'll take is an elementary example of the "calculus of variations". +The approach we'll take is an elementary example of the "calculus of variations". -Let's dive in and see what the key idea is. +Let's dive in and see what the key idea is. To explore what types of tax paths are cost-minimizing / welfare-improving, we shall create an **admissible tax path variation sequence** $\{v_t\}_{t=0}^S$ that satisfies @@ -508,7 +508,7 @@ $$ v_t = \xi_1 \phi^t - \xi_0. $$ -We say two and not three-parameter class because $\xi_0$ will be a function of $(\phi, \xi_1; R)$ that guarantees that the variation sequence is feasible. +We say two and not three-parameter class because $\xi_0$ will be a function of $(\phi, \xi_1; R)$ that guarantees that the variation sequence is feasible. Let's compute that function. @@ -521,7 +521,7 @@ $$ which implies that $$ -\xi_1 \sum_{t=0}^S \phi_t R^{-t} - \xi_0 \sum_{t=0}^S R^{-t} = 0 +\xi_1 \sum_{t=0}^S \phi^t R^{-t} - \xi_0 \sum_{t=0}^S R^{-t} = 0 $$ which implies that @@ -534,9 +534,9 @@ which implies that $$ \xi_0 = \xi_0(\phi, \xi_1; R) = \xi_1 \left(\frac{1 - R^{-1}}{1 - R^{-(S+1)}}\right) \left(\frac{1 - (\phi R^{-1})^{S+1}}{1 - \phi R^{-1}}\right) -$$ +$$ -This is our formula for $\xi_0$. +This is our formula for $\xi_0$. **Key Idea:** if $T^o$ is a budget-feasible tax path, then so is $T^o + v$, where $v$ is a budget-feasible variation. @@ -552,9 +552,9 @@ def compute_variation(model, ξ1, ϕ, B0, G_seq, verbose=1): ξ0 = ξ1*((1 - 1/R) / (1 - (1/R)**(S+1))) * ((1 - (ϕ/R)**(S+1)) / (1 - ϕ/R)) v_seq = np.array([(ξ1*ϕ**t - ξ0) for t in range(S+1)]) - + if verbose == 1: - print('check feasible:', np.isclose(β_seq @ v_seq, 0)) + print('check feasible:', np.isclose(β_seq @ v_seq, 0)) T_opt, _, _ = compute_optimal(model, B0, G_seq) Tvar_seq = T_opt + v_seq @@ -576,8 +576,8 @@ for i, param in enumerate(params): ξ1, ϕ = param print(f'variation {i}: ξ1={ξ1}, ϕ={ϕ}') - Tvar_seq = compute_variation(model=tax_model, - ξ1=ξ1, ϕ=ϕ, B0=B0, + Tvar_seq = compute_variation(model=tax_model, + ξ1=ξ1, ϕ=ϕ, B0=B0, G_seq=G_seq) print(f'welfare={welfare(tax_model, Tvar_seq)}') print(f'welfare < optimal: {welfare(tax_model, Tvar_seq) < wel_opt}') @@ -585,13 +585,13 @@ for i, param in enumerate(params): if i % 2 == 0: ls = '-.' - else: - ls = '-' - ax.plot(range(S+1), Tvar_seq, ls=ls, - color=colors[ξ1], + else: + ls = '-' + ax.plot(range(S+1), Tvar_seq, ls=ls, + color=colors[ξ1], label=fr'$\xi_1 = {ξ1}, \phi = {ϕ}$') -plt.plot(range(S+1), T_seq, +plt.plot(range(S+1), T_seq, color='orange', label=r'Optimal $\vec{T}$ ') plt.legend() @@ -600,7 +600,7 @@ plt.ylabel(r'$T_t$') plt.show() ``` -We can even use the Python `np.gradient` command to compute derivatives of cost with respect to our two parameters. +We can even use the Python `np.gradient` command to compute derivatives of cost with respect to our two parameters. We are teaching the key idea beneath the **calculus of variations**. First, we define the cost with respect to $\xi_1$ and $\phi$ @@ -608,13 +608,13 @@ First, we define the cost with respect to $\xi_1$ and $\phi$ ```{code-cell} ipython3 def cost_rel(ξ1, ϕ): """ - Compute cost of variation sequence + Compute cost of variation sequence for given ϕ, ξ1 with a tax-smoothing model """ - - Tvar_seq = compute_variation(tax_model, ξ1=ξ1, - ϕ=ϕ, B0=B0, - G_seq=G_seq, + + Tvar_seq = compute_variation(tax_model, ξ1=ξ1, + ϕ=ϕ, B0=B0, + G_seq=G_seq, verbose=0) return cost(tax_model, Tvar_seq) @@ -652,8 +652,226 @@ plt.show() cost_grad = cost_vec(0.05, ϕ_arr) cost_grad = np.gradient(cost_grad) -plt.plot(ξ1_arr, cost_grad) +plt.plot(ϕ_arr, cost_grad) plt.ylabel('derivative of cost') plt.xlabel(r'$\phi$') plt.show() -``` \ No newline at end of file +``` + +## Exercises + +```{exercise} +:label: tax_ex1 + +For the default expenditure sequence and $B_0 = 0$, suppose a one-time spending +spike of size $W_0 = 1$ occurs at period $t^* = 20$. + +a. Compute the optimal constant tax rate $T_0$ for the baseline expenditure + sequence (without the spike) and for the sequence with the spike, then print + the difference $\Delta T_0$. + +b. The present value of the spike is $W_0 R^{-t^*}$. Show analytically that the + increase in the optimal flat tax equals the **annuity value** of that present + value: + + $$ + \Delta T_0 = \left(\frac{1 - R^{-1}}{1 - R^{-(S+1)}}\right) W_0 R^{-t^*} + $$ + + Verify numerically that the formula matches the computed $\Delta T_0$. +``` + +```{solution-start} tax_ex1 +:class: dropdown +``` + +```{code-cell} ipython3 +tax_model = create_tax_smoothing_model() +S, R = tax_model.S, tax_model.R +B0 = 0 + +G_base = np.concatenate([np.ones(46), 4*np.ones(5), np.ones(15)]) +T_base, _, _ = compute_optimal(tax_model, B0, G_base) + +t_star = 20 +W0 = 1.0 +G_shock = G_base.copy() +G_shock[t_star] += W0 +T_shock, _, _ = compute_optimal(tax_model, B0, G_shock) + +delta_T0_numerical = T_shock[0] - T_base[0] + +annuity_factor = (1 - 1/R) / (1 - (1/R)**(S+1)) +delta_T0_formula = annuity_factor * W0 * R**(-t_star) + +print(f'Numerical ΔT0: {delta_T0_numerical:.10f}') +print(f'Annuity-value formula: {delta_T0_formula:.10f}') +print(f'Match: {np.isclose(delta_T0_numerical, delta_T0_formula)}') +``` + +The spike raises $h_0$, the present value of expenditures, by exactly $W_0 R^{-t^*}$. + +Since the optimal flat tax is the annuity value of $(B_0 + h_0)$, the increase +in $T_0$ is the annuity value of the extra present-value cost. + +```{solution-end} +``` + +```{exercise} +:label: tax_ex2 + +Using `compute_optimal`, compute the optimal flat tax $T_0$ for four geometrically +growing expenditure paths $G_t = \lambda^t G_0$ (for the working years +$t = 0, \ldots, 45$, then $G_t = 0$ thereafter), with $G_0 = 1$ and +$\lambda \in \{0.95,\, 1.00,\, 1.05,\, 1.10\}$. + +a. Plot the four expenditure sequences on one set of axes and the corresponding + debt paths $B_t$ on a second set. + +b. Identify which $\lambda$ requires the highest flat tax and explain why in + terms of the present-value formula for $h_0$. +``` + +```{solution-start} tax_ex2 +:class: dropdown +``` + +```{code-cell} ipython3 +tax_model = create_tax_smoothing_model() +S = tax_model.S +B0 = 2 +t_max = 46 +λ_vals = [0.95, 1.00, 1.05, 1.10] + +fig, axes = plt.subplots(1, 2, figsize=(12, 4)) + +for λ in λ_vals: + G_seq = np.concatenate( + [λ**np.arange(t_max), np.zeros(S + 1 - t_max)]) + T_seq, B_seq, _ = compute_optimal(tax_model, B0, G_seq) + axes[0].plot(range(S+1), G_seq, label=f'λ = {λ}') + axes[1].plot(range(S+2), B_seq, + label=f'λ = {λ}, T0 = {T_seq[0]:.3f}') + +for ax in axes: + ax.legend(fontsize=8) + ax.set_xlabel('$t$') +axes[0].set_ylabel('$G_t$') +axes[0].set_title('Expenditure sequences') +axes[1].set_ylabel('$B_t$') +axes[1].set_title('Debt paths') + +plt.tight_layout() +plt.show() + +print(f"{'λ':>6} | {'T0':>10} | {'h0':>12}") +print('-' * 34) +for λ in λ_vals: + G_seq = np.concatenate( + [λ**np.arange(t_max), np.zeros(S + 1 - t_max)]) + T_seq, _, h0 = compute_optimal(tax_model, B0, G_seq) + print(f'{λ:>6.2f} | {T_seq[0]:>10.4f} | {h0:>12.4f}') +``` + +Faster spending growth, or a higher $\lambda$, leaves initial spending unchanged but raises later spending enough to increase the present value $h_0$ despite discounting. + +A higher $h_0$ translates directly into a higher required flat tax. + +```{solution-end} +``` + +```{exercise} +:label: tax_ex3 + +Show numerically that the constant (optimal) tax path yields strictly lower cost +{eq}`cost`, equivalently, strictly higher welfare {eq}`welfare_tax`, than any +non-constant budget-feasible tax path. + +Use the default expenditure sequence and $B_0 = 2$ to evaluate welfare for the +optimal flat path and for four variations generated by `compute_variation` with +$(\xi_1, \phi) \in \{(0.2, 0.95),\, (0.2, 1.05),\, (-0.2, 0.90),\, (-0.2, 1.10)\}$, +then print a table showing each path's welfare and its gap relative to the optimum. +``` + +```{solution-start} tax_ex3 +:class: dropdown +``` + +```{code-cell} ipython3 +tax_model = create_tax_smoothing_model() +S = tax_model.S +B0 = 2 +G_seq = np.concatenate([np.ones(46), 4*np.ones(5), np.ones(15)]) + +T_seq, _, _ = compute_optimal(tax_model, B0, G_seq) +w_opt = welfare(tax_model, T_seq) + +print(f'Optimal (flat) welfare: {w_opt:.6f}\n') + +params = [(0.2, 0.95), (0.2, 1.05), (-0.2, 0.90), (-0.2, 1.10)] +print(f'{"ξ1":>6} | {"ϕ":>6} | {"welfare":>12} | {"gap":>14}') +print('-' * 46) +for ξ1, ϕ in params: + Tvar = compute_variation(tax_model, ξ1=ξ1, ϕ=ϕ, + B0=B0, G_seq=G_seq, verbose=0) + w = welfare(tax_model, Tvar) + print(f'{ξ1:>6.2f} | {ϕ:>6.2f} | {w:>12.6f} | {w - w_opt:>+14.6f}') +``` + +Every variation yields strictly lower welfare, or a more negative gap, than the flat path. + +This confirms that the constant tax schedule is the global welfare maximiser +under the quadratic criterion {eq}`welfare_tax` when $\beta R = 1$. + +```{solution-end} +``` + +```{exercise} +:label: tax_ex4 + +Show that the optimal flat tax $T_0$ is an affine (linear) function of initial +government debt $B_0$. + +For the default expenditure sequence $G$, compute $T_0$ for +$B_0 \in \{-4, -2, 0, 2, 4, 6\}$, plot $T_0$ against $B_0$, and verify that the +slope equals the annuity factor $\left(\frac{1-R^{-1}}{1-R^{-(S+1)}}\right)$. +``` + +```{solution-start} tax_ex4 +:class: dropdown +``` + +```{code-cell} ipython3 +tax_model = create_tax_smoothing_model() +S, R = tax_model.S, tax_model.R +G_seq = np.concatenate([np.ones(46), 4*np.ones(5), np.ones(15)]) + +B0_vals = [-4, -2, 0, 2, 4, 6] +T0_vals = [] + +for B0 in B0_vals: + T_seq, _, _ = compute_optimal(tax_model, B0, G_seq) + T0_vals.append(T_seq[0]) + +fig, ax = plt.subplots() +ax.plot(B0_vals, T0_vals, 'o-') +ax.set_xlabel('$B_0$ (initial government debt)') +ax.set_ylabel('Optimal flat tax $T_0$') +ax.set_title('$T_0$ is an affine function of $B_0$') +plt.show() + +slope = (T0_vals[-1] - T0_vals[0]) / (B0_vals[-1] - B0_vals[0]) +annuity = (1 - 1/R) / (1 - (1/R)**(S+1)) +print(f'Numerical slope of T0 w.r.t. B0: {slope:.8f}') +print(f'Annuity factor: {annuity:.8f}') +print(f'Match: {np.isclose(slope, annuity)}') +``` + +From equation {eq}`eq:taxsmoothing`, $T_0 = \text{annuity factor} \times (B_0 + h_0)$. + +The coefficient on $B_0$ is exactly the annuity factor, confirming the linear relationship. + +Higher initial debt forces the government to collect more tax in every period to satisfy its intertemporal budget constraint. + +```{solution-end} +``` diff --git a/lectures/time_series_with_matrices.md b/lectures/time_series_with_matrices.md index ab81f58b8..e32f07429 100644 --- a/lectures/time_series_with_matrices.md +++ b/lectures/time_series_with_matrices.md @@ -26,8 +26,8 @@ kernelspec: This lecture uses matrices to solve some linear difference equations. -As a running example, we’ll study a **second-order linear difference -equation** that was the key technical tool in Paul Samuelson’s 1939 +As a running example, we'll study a **second-order linear difference +equation** that was the key technical tool in Paul Samuelson's 1939 article {cite}`Samuelson1939` that introduced the *multiplier-accelerator model*. This model became the workhorse that powered early econometric versions of @@ -87,7 +87,7 @@ either of two *initial conditions*, two *terminal conditions* or possibly one of each. ``` -Let’s write our equations as a stacked system +Let's write our equations as a stacked system $$ \underset{\equiv A}{\underbrace{\left[\begin{array}{cccccccc} @@ -134,8 +134,8 @@ $$ The vector $y$ is a complete time path $\{y_t\}_{t=1}^T$. -Let’s put Python to work on an example that captures the flavor of -Samuelson’s multiplier-accelerator model. +Let's put Python to work on an example that captures the flavor of +Samuelson's multiplier-accelerator model. We'll set parameters equal to the same values we used in {doc}`intermediate:samuelson`. @@ -169,17 +169,17 @@ b[0] = α_0 + α_1 * y_0 + α_2 * y_neg1 b[1] = α_0 + α_2 * y_0 ``` -Let’s look at the matrix $A$ and the vector $b$ for our +Let's look at the matrix $A$ and the vector $b$ for our example. ```{code-cell} ipython3 A, b ``` -Now let’s solve for the path of $y$. +Now let's solve for the path of $y$. If $y_t$ is GNP at time $t$, then we have a version of -Samuelson’s model of the dynamics for GNP. +Samuelson's model of the dynamics for GNP. To solve $y = A^{-1} b$ we can either invert $A$ directly, as in @@ -275,10 +275,10 @@ y_{t} = \alpha_{0} + \alpha_{1} y_{t-1} + \alpha_{2} y_{t-2} + u_t where $u_{t} \sim N\left(0, \sigma_{u}^{2}\right)$ and is {ref}`IID `, meaning independent and identically distributed. -We’ll stack these $T$ equations into a system cast in terms of +We'll stack these $T$ equations into a system cast in terms of matrix algebra. -Let’s define the random vector +Let's define the random vector $$ u=\left[\begin{array}{c} @@ -302,7 +302,7 @@ $$ y = A^{-1} \left(b + u\right) $$ (eq:eqma) -Let’s try it out in Python. +Let's try it out in Python. ```{code-cell} ipython3 rng = np.random.default_rng() @@ -487,7 +487,7 @@ plt.ylabel('y') plt.show() ``` -Visually, notice how the variance across realizations of $y_t$ decreases as $t$ increases. +Because the initial conditions are fixed and shocks accumulate over time, the population variance of $y_t$ increases toward its limiting value. Let's plot the population variance of $y_t$ against $t$. @@ -581,10 +581,10 @@ Just as system {eq}`eq:eqma` constitutes a ## A forward looking model -Samuelson’s model is *backward looking* in the sense that we give it *initial conditions* and let it +Samuelson's model is *backward looking* in the sense that we give it *initial conditions* and let it run. -Let’s now turn to model that is *forward looking*. +Let's now turn to model that is *forward looking*. We apply similar linear algebra machinery to study a *perfect foresight* model widely used as a benchmark in macroeconomics and diff --git a/lectures/unpleasant.md b/lectures/unpleasant.md index 2edde85eb..d911590b2 100644 --- a/lectures/unpleasant.md +++ b/lectures/unpleasant.md @@ -11,34 +11,34 @@ kernelspec: name: python3 --- -# Some Unpleasant Monetarist Arithmetic +# Some Unpleasant Monetarist Arithmetic ## Overview This lecture builds on concepts and issues introduced in {doc}`money_inflation`. -That lecture describes stationary equilibria that reveal a [*Laffer curve*](https://en.wikipedia.org/wiki/Laffer_curve) in the inflation tax rate and the associated stationary rate of return -on currency. +That lecture describes stationary equilibria that reveal a [*Laffer curve*](https://en.wikipedia.org/wiki/Laffer_curve) in the inflation tax rate and the associated stationary rate of return +on currency. -In this lecture we study a situation in which a stationary equilibrium prevails after date $T > 0$, but not before then. +In this lecture we study a situation in which a stationary equilibrium prevails after date $T > 0$, but not before then. For $t=0, \ldots, T-1$, the money supply, price level, and interest-bearing government debt vary along a transition path that ends at $t=T$. -During this transition, the ratio of the real balances $\frac{m_{t+1}}{{p_t}}$ to indexed one-period government bonds $\tilde R B_{t-1}$ maturing at time $t$ decreases each period. +During this transition, the ratio of the real balances $\frac{m_{t+1}}{{p_t}}$ to indexed one-period government bonds $\tilde R B_{t-1}$ maturing at time $t$ decreases each period. -This has consequences for the **gross-of-interest** government deficit that must be financed by printing money for times $t \geq T$. +This has consequences for the **gross-of-interest** government deficit that must be financed by printing money for times $t \geq T$. The critical **money-to-bonds** ratio stabilizes only at time $T$ and afterwards. And the larger is $T$, the higher is the gross-of-interest government deficit that must be financed -by printing money at times $t \geq T$. +by printing money at times $t \geq T$. These outcomes are the essential finding of Sargent and Wallace's "unpleasant monetarist arithmetic" {cite}`sargent1981`. That lecture described supplies and demands for money that appear in that lecture. -It also characterized the steady state equilibrium from which we work backwards in this lecture. +It also characterized the steady state equilibrium from which we work backwards in this lecture. In addition to learning about "unpleasant monetarist arithmetic", in this lecture we'll learn how to implement a [*fixed point*](https://en.wikipedia.org/wiki/Fixed_point_(mathematics)) algorithm for computing an initial price level. @@ -67,17 +67,17 @@ where * $b_t = \frac{m_{t+1}}{p_t}$ is real balances at the end of period $t$ * $R_{t-1} = \frac{p_{t-1}}{p_t}$ is the gross rate of return on real balances held from $t-1$ to $t$ -The demand for real balances is +The demand for real balances is $$ -b_t = \gamma_1 - \gamma_2 R_t^{-1} . +b_t = \gamma_1 - \gamma_2 R_t^{-1} . $$ (eq:up_bdemand) where $\gamma_1 > \gamma_2 > 0$. ## Monetary-Fiscal Policy -To the basic model of {doc}`money_inflation`, we add inflation-indexed one-period government bonds as an additional way for the government to finance government expenditures. +To the basic model of {doc}`money_inflation`, we add inflation-indexed one-period government bonds as an additional way for the government to finance government expenditures. Let $\widetilde R > 1$ be a time-invariant gross real rate of return on government one-period inflation-indexed bonds. @@ -85,7 +85,7 @@ With this additional source of funds, the government's budget constraint at time $$ B_t + \frac{m_{t+1}}{p_t} = \widetilde R B_{t-1} + \frac{m_t}{p_t} + g -$$ +$$ Just before the beginning of time $0$, the public owns $\check m_0$ units of currency (measured in dollars) @@ -106,17 +106,17 @@ $$ or $$ -B_{-1} - \check B_{-1} = \frac{1}{p_0 \widetilde R} \left( \check m_0 - m_0 \right) +B_{-1} - \check B_{-1} = \frac{1}{p_0 \widetilde R} \left( \check m_0 - m_0 \right) $$ (eq:openmarketconstraint) -This equation says that the government (e.g., the central bank) can *decrease* $m_0$ relative to -$\check m_0$ by *increasing* $B_{-1}$ relative to $\check B_{-1}$. +This equation says that the government (e.g., the central bank) can *decrease* $m_0$ relative to +$\check m_0$ by *increasing* $B_{-1}$ relative to $\check B_{-1}$. -This is a version of a standard constraint on a central bank's [**open market operations**](https://www.federalreserve.gov/monetarypolicy/openmarket.htm) in which it expands the stock of money by buying government bonds from the public. +This is a version of a standard constraint on a central bank's [**open market operations**](https://www.federalreserve.gov/monetarypolicy/openmarket.htm) in which it expands the stock of money by buying government bonds from the public. ## An open market operation at $t=0$ -Following Sargent and Wallace {cite}`sargent1981`, we analyze consequences of a central bank policy that +Following Sargent and Wallace {cite}`sargent1981`, we analyze consequences of a central bank policy that uses an open market operation to lower the price level in the face of a persistent fiscal deficit that takes the form of a positive $g$. @@ -128,7 +128,7 @@ For $t =0, 1, \ldots, T-1$, $$ \begin{aligned} B_t & = \widetilde R B_{t-1} + g \cr -m_{t+1} & = m_0 +m_{t+1} & = m_0 \end{aligned} $$ @@ -141,7 +141,7 @@ m_{t+1} & = m_t + p_t \overline g \end{aligned} $$ -where +where $$ \overline g = \left[(\tilde R -1) B_{T-1} + g \right] @@ -155,7 +155,7 @@ Here, by **fiscal policy** we mean the collection of actions that determine a se By **monetary policy** or **debt-management policy**, we mean the collection of actions that determine how the government divides its portfolio of debts to the public between interest-bearing parts (government bonds) and non-interest-bearing parts (money). By an **open market operation**, we mean a government monetary policy action in which the government -(or its delegate, say, a central bank) either buys government bonds from the public for newly issued money, or sells bonds to the public and withdraws the money it receives from public circulation. +(or its delegate, say, a central bank) either buys government bonds from the public for newly issued money, or sells bonds to the public and withdraws the money it receives from public circulation. ## Algorithm (basic idea) @@ -171,7 +171,7 @@ $$ (eq:up_steadyquadratic) Quadratic equation {eq}`eq:up_steadyquadratic` has two roots, $R_l < R_u < 1$. -For reasons described at the end of {doc}`money_inflation`, we select the larger root $R_u$. +For reasons described at the end of {doc}`money_inflation`, we select the larger root $R_u$. Next, we compute @@ -192,14 +192,14 @@ $$ b_t & = b_{t-1} R_{t-1} + \overline g \cr R_t^{-1} & = \frac{\gamma_1}{\gamma_2} - \gamma_2^{-1} b_t \cr p_t & = R_t p_{t-1} \cr - m_t & = b_{t-1} p_t + m_t & = b_{t-1} p_t \end{aligned} $$ - -## Before time $T$ -Define +## Before time $T$ + +Define $$ \lambda \equiv \frac{\gamma_2}{\gamma_1}. @@ -209,7 +209,7 @@ Our restrictions that $\gamma_1 > \gamma_2 > 0$ imply that $\lambda \in [0,1)$. We want to compute -$$ +$$ \begin{aligned} p_0 & = \gamma_1^{-1} \left[ \sum_{j=0}^\infty \lambda^j m_{j} \right] \cr & = \gamma_1^{-1} \left[ \sum_{j=0}^{T-1} \lambda^j m_{0} + \sum_{j=T}^\infty \lambda^j m_{1+j} \right] @@ -234,7 +234,7 @@ $$ p_t = \gamma_1^{-1} m_0 + \lambda p_{t+1}, \quad t = T-1, T-2, \ldots, 0 $$ -starting from +starting from $$ p_T = \frac{m_0}{\gamma_1 - \overline g - \gamma_2 R_u^{-1}} = \gamma_1^{-1} m_0 \left\{\frac{1}{R_u-\lambda} \right\} @@ -242,14 +242,14 @@ $$ (eq:pTformula) ```{prf:remark} :label: equivalence -We can verify the equivalence of the two formulas on the right sides of {eq}`eq:pTformula` by recalling that +We can verify the equivalence of the two formulas on the right sides of {eq}`eq:pTformula` by recalling that $R_u$ is a root of the quadratic equation {eq}`eq:up_steadyquadratic` that determines steady state rates of return on currency. ``` - + ## Algorithm (pseudo code) Now let's describe a computational algorithm in more detail in the form of a description -that constitutes pseudo code because it approaches a set of instructions we could provide to a +that constitutes pseudo code because it approaches a set of instructions we could provide to a Python coder. To compute an equilibrium, we deploy the following algorithm. @@ -262,7 +262,7 @@ We define a mapping from $p_0$ to $\widehat p_0$ as follows. * Set $m_0$ and then compute $B_{-1}$ to satisfy the constraint on time $0$ **open market operations** $$ -B_{-1}- \check B_{-1} = \frac{\widetilde R}{p_0} \left( \check m_0 - m_0 \right) +B_{-1}- \check B_{-1} = \frac{1}{p_0\widetilde R} \left( \check m_0 - m_0 \right) $$ * Compute $B_{T-1}$ from @@ -271,7 +271,7 @@ $$ B_{T-1} = \widetilde R^T B_{-1} + \left( \frac{1 - \widetilde R^T}{1-\widetilde R} \right) g $$ -* Compute +* Compute $$ \overline g = g + \left[ \tilde R - 1\right] B_{T-1} @@ -294,7 +294,7 @@ $$ * Compute a fixed point by iterating to convergence on the relaxation algorithm $$ -p_{0,j+1} = (1-\theta) {\mathcal S}(p_{0,j}) + \theta p_{0,j}, +p_{0,j+1} = (1-\theta) {\mathcal S}(p_{0,j}) + \theta p_{0,j}, $$ where $\theta \in [0,1)$ is a relaxation parameter. @@ -305,20 +305,21 @@ where $\theta \in [0,1)$ is a relaxation parameter. We'll set parameters of the model so that the steady state after time $T$ is initially the same as in {doc}`money_inflation_nonlinear` -In particular, we set $\gamma_1=100, \gamma_2 =50, g=3.0$. We set $m_0 = 100$ in that lecture, -but now the counterpart will be $M_T$, which is endogenous. +In particular, we set $\gamma_1=100, \gamma_2 =50, g=3.0$. + +We set $m_0 = 100$ in that lecture, but now the counterpart will be $M_T$, which is endogenous. As for new parameters, we'll set $\tilde R = 1.01, \check B_{-1} = 0, \check m_0 = 105, T = 5$. We'll study a "small" open market operation by setting $m_0 = 100$. -These parameter settings mean that just before time $0$, the "central bank" sells the public bonds in exchange for $\check m_0 - m_0 = 5$ units of currency. +These parameter settings mean that just before time $0$, the "central bank" sells the public bonds in exchange for $\check m_0 - m_0 = 5$ units of currency. That leaves the public with less currency but more government interest-bearing bonds. Since the public has less currency (its supply has diminished) it is plausible to anticipate that the price level at time $0$ will be driven downward. -But that is not the end of the story, because this **open market operation** at time $0$ has consequences for future settings of $m_{t+1}$ and the gross-of-interest government deficit $\bar g_t$. +But that is not the end of the story, because this **open market operation** at time $0$ has consequences for future settings of $m_{t+1}$ and the gross-of-interest government deficit $\bar g_t$. Let's start with some imports: @@ -333,7 +334,7 @@ Now let's dive in and implement our pseudo code in Python. ```{code-cell} ipython3 # Create a namedtuple that contains parameters -MoneySupplyModel = namedtuple("MoneySupplyModel", +MoneySupplyModel = namedtuple("MoneySupplyModel", ["γ1", "γ2", "g", "R_tilde", "m0_check", "Bm1_check", "T"]) @@ -342,7 +343,7 @@ def create_model(γ1=100, γ2=50, g=3.0, R_tilde=1.01, Bm1_check=0, m0_check=105, T=5): - + return MoneySupplyModel(γ1=γ1, γ2=γ2, g=g, R_tilde=R_tilde, m0_check=m0_check, Bm1_check=Bm1_check, @@ -397,7 +398,7 @@ def compute_fixed_point(m0, p0_guess, model, θ=0.5, tol=1e-6): ``` Let's look at how price level $p_0$ in the stationary $R_u$ equilibrium depends on the initial -money supply $m_0$. +money supply $m_0$. Notice that the slope of $p_0$ as a function of $m_0$ is constant. @@ -473,13 +474,13 @@ def plot_path(m0_arr, model, length=15): fig, axs = plt.subplots(2, 2, figsize=(8, 5)) titles = ['$p_t$', '$m_t$', '$b_t$', '$R_t$'] - + for m0 in m0_arr: paths = simulate(m0, model, length=length) for i, ax in enumerate(axs.flat): ax.plot(paths[i]) ax.set_title(titles[i]) - + axs[0, 1].hlines(model.m0_check, 0, length, color='r', linestyle='--') axs[0, 1].text(length * 0.8, model.m0_check * 0.9, r'$\check{m}_0$') plt.show() @@ -504,3 +505,221 @@ plot_path([80, 100], msm) * An open market operation that reduces the post open market operation money supply at time $0$ also *lowers* the rate of return on money $R_u$ at times $t \geq T$ because it brings a higher gross of interest government deficit that must be financed by printing money (i.e., levying an inflation tax) at time $t \geq T$. * $R$ is important in the context of maintaining monetary stability and addressing the consequences of increased inflation due to government deficits. Thus, a larger $R$ might be chosen to mitigate the negative impacts on the real rate of return caused by inflation. + +## Exercises + +```{exercise} +:label: un_ex1 + +**How the length of the tight-money period $T$ amplifies unpleasant arithmetic.** + +The lecture shows that a central bank open-market operation that reduces $m_0$ +at $t = 0$ lowers the price level immediately but forces a higher +post-$T$ deficit $\bar g$ and therefore a lower rate of return $R_u$ (higher +inflation) forever after. + +The same mechanism operates as $T$ grows: holding $m_0 = 100$ fixed, a longer +period of bond-financed deficits accumulates more interest-bearing debt +$B_{T-1}$ that must eventually be serviced by printing money. + +Fix $m_0 = 100$ and vary $T \in \{1, 3, 5, 10, 20\}$. For each $T$: + +a. Use `simulate` to obtain the stationary post-$T$ rate of return + $R_u$ (it equals `paths[3, T]`). + +b. Compute the post-$T$ government deficit + $\bar g = g + (\tilde R - 1) B_{T-1}$ directly from the model parameters + and the fixed-point $p_0$. + +c. Plot $R_u$ and $\bar g$ against $T$ on side-by-side panels and explain + why $R_u$ falls and $\bar g$ rises as $T$ increases. +``` + +```{solution-start} un_ex1 +:class: dropdown +``` + +```{code-cell} ipython3 +m0 = 100 +T_values = [1, 3, 5, 10, 20] + +R_u_list = [] +g_bar_list = [] + +for T_val in T_values: + model_T = create_model(T=T_val) + # equilibrium price level + p0 = compute_fixed_point(m0, 1, model_T) + # open-market operation creates bonds + Bm1 = (1 / (p0 * model_T.R_tilde)) * (model_T.m0_check - m0) \ + + model_T.Bm1_check + BTm1 = (model_T.R_tilde ** T_val * Bm1 + + (1 - model_T.R_tilde ** T_val) / (1 - model_T.R_tilde) * model_T.g) + g_bar = model_T.g + (model_T.R_tilde - 1) * BTm1 + g_bar_list.append(g_bar) + # post-T rate of return from simulate + paths = simulate(m0, model_T, length=T_val + 3) + R_u_list.append(paths[3, T_val]) + +fig, axes = plt.subplots(1, 2, figsize=(10, 4)) + +axes[0].plot(T_values, R_u_list, marker='o') +axes[0].set_xlabel('$T$') +axes[0].set_ylabel('$R_u$ (post-$T$ rate of return)') +axes[0].set_title('Longer tight-money period lowers $R_u$') + +axes[1].plot(T_values, g_bar_list, marker='o', color='tab:orange') +axes[1].set_xlabel('$T$') +axes[1].set_ylabel(r'$\bar{g}$ (post-$T$ deficit)') +axes[1].set_title('Longer tight-money period raises $\\bar{g}$') + +plt.tight_layout() +plt.show() + +print(f"\n{'T':>4} {'g_bar':>8} {'R_u':>8}") +print('-' * 26) +for T_val, g_b, Ru in zip(T_values, g_bar_list, R_u_list): + print(f"{T_val:>4} {g_b:>8.4f} {Ru:>8.4f}") +``` + +Each additional period that $m_t$ is held fixed forces the government to roll +over its bonds at gross rate $\tilde R > 1$, compounding the stock $B_{T-1}$ and therefore raising $\bar g$. + +A higher $\bar g$ sits further up the seigniorage Laffer curve, requiring a +lower $R_u$, or higher inflation tax rate. + +This is the core mechanism of "unpleasant monetarist arithmetic": tighter +money today makes the long-run inflation rate higher, not lower. + +```{solution-end} +``` + +```{exercise} +:label: un_ex2 + +**The fiscal limit: how large can $T$ be?** + +The post-$T$ deficit $\bar g$ must be financeable by the inflation tax, which +means it cannot exceed the maximum seigniorage revenue +$g_{\rm max} = S(\bar R_{\rm max})$ from the Laffer curve. + +With $m_0 = 100$ and the default parameters, find the **fiscal limit** +$T^*$: the largest integer $T$ for which a feasible stationary equilibrium +still exists after time $T$ (i.e., $\bar g \leq g_{\rm max}$). + +a. Compute $g_{\rm max} = (\gamma_1 + \gamma_2) - \gamma_2/\bar R_{\rm max} + - \gamma_1 \bar R_{\rm max}$ where $\bar R_{\rm max} = \sqrt{\gamma_2/\gamma_1}$. + +b. For $T = 1, 2, \ldots, 150$, compute $\bar g(T)$ as in {ref}`un_ex1`. + + Plot $\bar g(T)$ and $g_{\rm max}$ on the same axes and shade the infeasible region. + +c. Identify $T^*$, print $\bar g$ at $T^*$, and verify that no feasible real fixed point exists at $T^* + 1$. +``` + +```{solution-start} un_ex2 +:class: dropdown +``` + +```{code-cell} ipython3 +γ1, γ2 = msm.γ1, msm.γ2 + +# Part a: Laffer-curve peak +R_max = np.sqrt(γ2 / γ1) +g_max = (γ1 + γ2) - γ2 / R_max - γ1 * R_max +print(f"R_max = {R_max:.4f}") +print(f"g_max = {g_max:.4f}") + +# Part b: g_bar for T = 1 ... 150 +m0 = 100 +T_candidates = np.arange(1, 151) + +def S_with_g_bar(p0, m0, model): + γ1, γ2, g = model.γ1, model.γ2, model.g + R_tilde = model.R_tilde + m0_check, Bm1_check = model.m0_check, model.Bm1_check + T = model.T + + Bm1 = 1 / (p0 * R_tilde) * (m0_check - m0) + Bm1_check + BTm1 = (R_tilde ** T * Bm1 + + ((1 - R_tilde ** T) / (1 - R_tilde)) * g) + g_bar = g + (R_tilde - 1) * BTm1 + + disc = (γ1 + γ2 - g_bar)**2 - 4 * γ1 * γ2 + if disc < 0: + return np.nan, np.nan, False + + Ru = ((γ1 + γ2 - g_bar) + np.sqrt(disc)) / (2 * γ1) + λ = γ2 / γ1 + p0_new = (1 / γ1) * m0 * ( + (1 - λ ** T) / (1 - λ) + λ ** T / (Ru - λ)) + + return p0_new, g_bar, True + +def compute_fixed_point_and_g_bar(T_val, m0, p0_guess=1, + θ=0.5, tol=1e-6, max_iter=10_000): + model_T = create_model(T=int(T_val)) + p0 = p0_guess + + for _ in range(max_iter): + p0_new, g_bar, real_roots = S_with_g_bar(p0, m0, model_T) + if not real_roots: + return np.nan, np.nan, False + + p0_next = (1 - θ) * p0_new + θ * p0 + if np.abs(p0_next - p0) < tol: + _, g_bar, real_roots = S_with_g_bar(p0_next, m0, model_T) + return p0_next, g_bar, real_roots + + p0 = p0_next + + return np.nan, np.nan, False + +g_bar_arr = np.full(len(T_candidates), np.nan) +p0_guess = 1 + +for i, T_val in enumerate(T_candidates): + p0_star, g_bar, real_roots = compute_fixed_point_and_g_bar( + T_val, m0, p0_guess=p0_guess) + if real_roots: + g_bar_arr[i] = g_bar + p0_guess = p0_star + +finite = np.isfinite(g_bar_arr) +feasible = finite & (g_bar_arr <= g_max) + +fig, ax = plt.subplots() +ax.plot(T_candidates[finite], g_bar_arr[finite], label=r'$\bar{g}(T)$') +ax.axhline(g_max, color='red', linestyle='--', + label=f'$g_{{\\rm max}} = {g_max:.2f}$') +if np.any(feasible): + T_star = T_candidates[feasible][-1] + ax.axvspan(T_star + 1, T_candidates[-1], alpha=0.15, color='red', + label='infeasible region') +ax.set_xlabel('$T$') +ax.set_ylabel(r'post-$T$ deficit $\bar{g}$') +ax.set_title('Fiscal limit: $\\bar{g}(T)$ vs Laffer-curve maximum') +ax.legend() +plt.tight_layout() +plt.show() + +# Part c: fiscal limit T* +p0_next, g_bar_next, real_next = compute_fixed_point_and_g_bar( + T_star + 1, m0, p0_guess=p0_guess) + +print(f"\nFiscal limit T* = {T_star}") +print(f" g_bar(T*) = {g_bar_arr[T_star - 1]:.4f} <= g_max = {g_max:.4f}") +print(f" Feasible real fixed point at T*+1 = {T_star + 1}: {real_next}") +``` + +Beyond $T^*$, the accumulated bond debt $B_{T-1}$ is so large that the +implied $\bar g$ exceeds the peak of the seigniorage Laffer curve. + +No matter how high the government sets the inflation tax rate, it cannot +raise enough revenue to service that debt. + +The fiscal limit is therefore a hard constraint on how long a tight-money +policy can be pursued before the underlying fiscal arithmetic becomes incoherent. + +```{solution-end} +```