@@ -11,13 +11,20 @@ kernelspec:
1111 name : python3
1212---
1313
14+ +++ {"user_expressions": [ ] }
15+
1416## Some dynamic models with matrices
1517
1618In this notebook, we'll present some useful models of economic dynamics using only linear algebra -- matrix multiplication and matrix inversion.
1719
1820** Present value formulas** are at the core of the models.
1921
20- +++
22+ ``` {code-cell} ipython3
23+ import numpy as np
24+ import matplotlib.pyplot as plt
25+ ```
26+
27+ +++ {"user_expressions": [ ] }
2128
2229## Consumption smoothing
2330
@@ -71,7 +78,7 @@ where $g_1 > 0, g_2 > 0$.
7178
7279We shall see that when $\beta R = 1$ (a condition assumed by Milton Friedman and Robert Hall), this criterion assigns higher welfare to ** smoother** consumption paths.
7380
74- +++
81+ +++ {"user_expressions": [ ] }
7582
7683## Difference equations with linear algebra ##
7784
@@ -94,14 +101,20 @@ where $y_0$ is a given initial condition.
94101We can cast this set of $T$ equations as a single matrix equation
95102
96103$$
97- \begin{bmatrix} 1 & 0 & 0 & \cdots & 0 & 0 \cr
98- -\lambda & 1 & 0 & \cdots & 0 & 0 \cr
99- 0 & -\lambda & 1 & \cdots & 0 & 0 \cr
100- \vdots & \vdots & \vdots & \cdots & \vdots & \vdots \cr
101- 0 & 0 & 0 & \cdots & -\lambda & 1
102-
103- \end{bmatrix} \begin{bmatrix} y_1 \cr y_2 \cr y_3 \cr \vdots \cr y_T \end{bmatrix}
104- = \begin{bmatrix} \lambda y_0 \cr 0 \cr 0 \cr \vdots \cr 0 \end{bmatrix}
104+ \begin{bmatrix}
105+ 1 & 0 & 0 & \cdots & 0 & 0 \cr
106+ -\lambda & 1 & 0 & \cdots & 0 & 0 \cr
107+ 0 & -\lambda & 1 & \cdots & 0 & 0 \cr
108+ \vdots & \vdots & \vdots & \cdots & \vdots & \vdots \cr
109+ 0 & 0 & 0 & \cdots & -\lambda & 1
110+ \end{bmatrix}
111+ \begin{bmatrix}
112+ y_1 \cr y_2 \cr y_3 \cr \vdots \cr y_T
113+ \end{bmatrix}
114+ =
115+ \begin{bmatrix}
116+ \lambda y_0 \cr 0 \cr 0 \cr \vdots \cr 0
117+ \end{bmatrix}
105118$$
106119
107120
110123Multiplying both sides by inverse of the matrix on the left provides the solution
111124
112125$$
113- \begin{bmatrix} y_1 \cr y_2 \cr y_3 \cr \vdots \cr y_T \end{bmatrix} =
114- \begin{bmatrix} 1 & 0 & 0 & \cdots & 0 & 0 \cr
115- \lambda & 1 & 0 & \cdots & 0 & 0 \cr
116- \lambda^2 & \lambda & 1 & \cdots & 0 & 0 \cr
117- \vdots & \vdots & \vdots & \cdots & \vdots & \vdots \cr
118- \lambda^{T-1} & \lambda^{T-2} & \lambda^{T-3} & \cdots & -\lambda & 1
126+ \begin{bmatrix}
127+ y_1 \cr y_2 \cr y_3 \cr \vdots \cr y_T
128+ \end{bmatrix}
129+ =
130+ \begin{bmatrix}
131+ 1 & 0 & 0 & \cdots & 0 & 0 \cr
132+ \lambda & 1 & 0 & \cdots & 0 & 0 \cr
133+ \lambda^2 & \lambda & 1 & \cdots & 0 & 0 \cr
134+ \vdots & \vdots & \vdots & \cdots & \vdots & \vdots \cr
135+ \lambda^{T-1} & \lambda^{T-2} & \lambda^{T-3} & \cdots & -\lambda & 1
136+ \end{bmatrix}
137+ \begin{bmatrix}
138+ \lambda y_0 \cr 0 \cr 0 \cr \vdots \cr 0
119139\end{bmatrix}
120- \begin{bmatrix} \lambda y_0 \cr 0 \cr 0 \cr \vdots \cr 0 \end{bmatrix}
121-
122140$$
123141
124142
125143#### Second order difference equation
126144
127145
128146$$
129- \begin{bmatrix} 1 & 0 & 0 & \cdots & 0 & 0 & 0 \cr
130- -\lambda_1 & 1 & 0 & \cdots & 0 & 0 & 0 \cr
131- -\lambda_2 & -\lambda_2 & 1 & \cdots & 0 & 0 & 0 \cr
132- \vdots & \vdots & \vdots & \cdots & \vdots & \vdots \cr
133- 0 & 0 & 0 & \cdots & \lambda_2 & -\lambda_1 & 1
134-
135- \end{bmatrix} \begin{bmatrix} y_1 \cr y_2 \cr y_3 \cr \vdots \cr y_T \end{bmatrix}
136- = \begin{bmatrix} \lambda_1 y_0 + \lambda_2 y_{-1} \cr \lambda_2 y_0 \cr 0 \cr \vdots \cr 0 \end{bmatrix}
147+ \begin{bmatrix}
148+ 1 & 0 & 0 & \cdots & 0 & 0 & 0 \cr
149+ -\lambda_1 & 1 & 0 & \cdots & 0 & 0 & 0 \cr
150+ -\lambda_2 & -\lambda_2 & 1 & \cdots & 0 & 0 & 0 \cr
151+ \vdots & \vdots & \vdots & \cdots & \vdots & \vdots \cr
152+ 0 & 0 & 0 & \cdots & \lambda_2 & -\lambda_1 & 1
153+ \end{bmatrix}
154+ \begin{bmatrix}
155+ y_1 \cr y_2 \cr y_3 \cr \vdots \cr y_T
156+ \end{bmatrix}
157+ =
158+ \begin{bmatrix}
159+ \lambda_1 y_0 + \lambda_2 y_{-1} \cr \lambda_2 y_0 \cr 0 \cr \vdots \cr 0
160+ \end{bmatrix}
137161$$
138162
139163Multiplying both sides by inverse of the matrix on the left again provides the solution.
@@ -143,7 +167,7 @@ Multiplying both sides by inverse of the matrix on the left again provides the
143167As an exercise, we ask you to represent and solve a ** third order linear difference equation** .
144168How many initial conditions must you specify?
145169
146- +++
170+ +++ {"user_expressions": [ ] }
147171
148172## Friedman-Hall consumption-smoothing model
149173
@@ -152,7 +176,7 @@ A key object is what Milton Friedman called "non-human" or "non-financial" wealt
152176
153177
154178$$
155- h_0 \equiv \sum_{t=0}^T R^t y_t = \begin{bmatrix} 1 & R & \cdots & R^T \end{bmatrix}
179+ h_0 \equiv \sum_{t=0}^T R^{-t} y_t = \begin{bmatrix} 1 & R^{-1} & \cdots & R^{-T} \end{bmatrix}
156180\begin{bmatrix} y_0 \cr y_1 \cr \vdots \cr y_T \end{bmatrix}
157181$$
158182
165189it is possible to convert a sequence of budget constraints into the single intertemporal constraint
166190
167191$$
168- \sum_{t=0}^T R^t c_t = a_0 + h_0,
192+ \sum_{t=0}^T R^{-t} c_t = a_0 + h_0,
169193$$
170194
171195which says that the present value of the consumption stream equals the sum of finanical and non-financial wealth.
180204In this case, we can use the intertemporal budget constraint to write
181205
182206$$
183- c_0 = \left(\sum_{t=0}^T R^t \right)^{-1} (a_0 + h_0)
207+ c_0 = \left(\sum_{t=0}^T R^{-t} \right)^{-1} (a_0 + h_0)
184208$$
185209
186210This is the consumption-smoothing model in a nutshell.
187211
188212We'll put the model through some paces with Python code below.
189213
190- +++
214+ +++ {"user_expressions": [ ] }
191215
192216## Permanent income model of consumption
193217
@@ -203,7 +227,7 @@ In the calculations below, please we'll set default values of $R > 1$, e.g., $
203227For some $T+1 \times 1$ $y$ vector, use matrix algebra to compute
204228
205229$$
206- \sum_{t=0}^T R^t y_t = \begin{bmatrix} 1 & R & \cdots & R^T \end{bmatrix}
230+ \sum_{t=0}^T R^{-t} y_t = \begin{bmatrix} 1 & R^{-1} & \cdots & R^{-T} \end{bmatrix}
207231\begin{bmatrix} y_0 \cr y_1 \cr \vdots \cr y_T \end{bmatrix}
208232$$
209233
215239c_0 = \left( \frac{1 - R^{-1}}{1 - R^{-(T+1)}} \right) (a_0 + \sum_{t=0}^T R^t y_t )
216240$$
217241
242+ ** Jiacheng:** The same for $R^t$ here.
218243
219244#### Step 3 ####
220245
228253\vdots &\vdots & \vdots & \cdots & \vdots & \vdots & \vdots \cr
2292540 & 0 & 0 & \cdots & -R & 1 & 0 \cr
2302550 & 0 & 0 & \cdots & 0 & -R & 1
231- \end{bmatrix} \begin{bmatrix} a_1 \cr a_2 \cr a_3 \cr \vdots \cr a_T \cr a_{T+1} \end{bmatrix}
232- = R \begin{bmatrix} y_0 + a_0 - c_0 \cr y_1 - c_0 \cr y_2 - c_0 \cr \vdots\cr y_T - y_0 \cr 0
256+ \end{bmatrix}
257+ \begin{bmatrix} a_1 \cr a_2 \cr a_3 \cr \vdots \cr a_T \cr a_{T+1}
258+ \end{bmatrix}
259+ = R
260+ \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
233261\end{bmatrix}
234-
235262$$
236263
237264Multiply both sides by the inverse of the matrix on the left side to compute
@@ -250,13 +277,14 @@ Let's verify this with our Python code.
250277
251278
252279
280+
253281### Feasible consumption variations ###
254282
255283To explore what types of consumption paths are welfare-improving, we shall create an ** admissible consumption path variation sequence** $\{ v_t\} _ {t=0}^T$
256284that satisfies
257285
258286$$
259- \sum_{t=0}^T v_t = 0
287+ \sum_{t=0}^T R^{-t} v_t = 0
260288$$
261289
262290We'll compute a two-parameter class of admissible variations
@@ -305,7 +333,155 @@ to compute alternative consumption paths, then evaluate their welfare.
305333** Note to John:** We can do some fun simple experiments with these variations -- we can use
306334graphs to show that, when $\beta R =1$ and starting from the smooth path, all nontrivial budget-feasible variations lower welfare according to the criterion above.
307335
308- We can even use the Python numpy grad command to compute derivatives of welfare with respect to our two parameters. Notice that we are teaching the key idea beneath the calculus of variations.
336+ We can even use the Python numpy grad command to compute derivatives of welfare with respect to our two parameters.
337+
338+ We are teaching the key idea beneath the ** calculus of variations** .
339+
340+ ``` {code-cell} ipython3
341+ class Consumption_smoothing:
342+ "A class of the Permanent Income model of consumption"
343+
344+ def __init__(self, R, y_seq, a0, g1, g2, T):
345+ self.a0, self.y_seq, self.R, self.β = a0, y_seq, R, 1/R # set β = 1/R
346+ self.g1, self.g2 = g1, g2 # welfare parameter
347+ self.T = T
348+
349+ self.β_seq = np.array([self.β**i for i in range(T+1)])
350+
351+ def compute_optimal(self, verbose=1):
352+ R, y_seq, a0, T = self.R, self.y_seq, self.a0, self.T
353+
354+ # non-financial wealth
355+ h0 = self.β_seq @ y_seq # since β = 1/R
356+
357+ # c0
358+ c0 = (1 - 1/R) / (1 - (1/R)**(T+1)) * (a0 + h0)
359+ c_seq = c0*np.ones(T+1)
360+
361+ # verify
362+ A = np.diag(-R*np.ones(T), k=-1) + np.eye(T+1)
363+ b = y_seq - c_seq
364+ b[0] = b[0] + a0
365+
366+ a_seq = np.linalg.inv(A) @ b
367+ a_seq = np.concatenate([[a0], a_seq])
368+
369+ # check that a_T+1 = 0
370+ if verbose==1:
371+ print('check a_T+1=0:', np.abs(a_seq[-1] - 0) <= 1e-8)
372+
373+ return c_seq, a_seq
374+
375+ def welfare(self, c_seq):
376+ β_seq, g1, g2 = self.β_seq, self.g1, self.g2
377+
378+ u_seq = g1 * c_seq - g2/2 * c_seq**2
379+ return β_seq @ u_seq
380+
381+
382+ def compute_variation(self, ξ1, ϕ, verbose=1):
383+ R, T, β_seq = self.R, self.T, self.β_seq
384+
385+ ξ0 = ξ1*((1 - 1/R) / (1 - (1/R)**(T+1))) * ((1 - (ϕ/R)**(T+1)) / (1 - ϕ/R))
386+ v_seq = np.array([(ξ1*ϕ**t - ξ0) for t in range(T+1)])
387+
388+ # check if it is feasible
389+ if verbose==1:
390+ print('check feasible:', np.round(β_seq @ v_seq, 7)==0) # since β = 1/R
391+
392+ c_opt, _ = self.compute_optimal(verbose=verbose)
393+ cvar_seq = c_opt + v_seq
394+
395+ return cvar_seq
396+ ```
397+
398+ +++ {"user_expressions": [ ] }
399+
400+ Below is an example where the consumer inherits $a_0<0$ (which can be interpreted as a student debt).
401+
402+ The income process $\{ y_t\} _ {t=0}^{T}$ is constant and positive up to $t=45$ and then becomes zero afterward.
403+
404+ ``` {code-cell} ipython3
405+ # parameters
406+ T=65
407+ R = 1.05
408+ g1 = 1
409+ g2 = 1/2
410+
411+ # financial wealth
412+ a0 = -2 # such as "student debt"
413+
414+ # income process
415+ y_seq = np.concatenate([np.ones(46), np.zeros(20)])
416+
417+ # create an instance
418+ mc = Consumption_smoothing(R=R, y_seq=y_seq, a0=a0, g1=g1, g2=g2, T=T)
419+ c_seq, a_seq = mc.compute_optimal()
420+
421+ # compute welfare
422+ print('Welfare:', mc.welfare(c_seq))
423+ ```
424+
425+ ``` {code-cell} ipython3
426+ plt.plot(range(T+1), y_seq, label='income')
427+ plt.plot(range(T+1), c_seq, label='consumption')
428+ plt.plot(range(T+2), a_seq, label='asset')
429+ plt.plot(range(T+2), np.zeros(T+2), '--')
430+
431+ plt.legend()
432+ plt.xlabel(r'$t$')
433+ plt.ylabel(r'$c_t,y_t,a_t$')
434+ plt.show()
435+ ```
436+
437+ +++ {"user_expressions": [ ] }
438+
439+ We can visualize how $\xi_1$ and $\phi$ controls ** budget-feasible variations** .
440+
441+ ``` {code-cell} ipython3
442+ # visualize variational paths
443+ cvar_seq1 = mc.compute_variation(ξ1=.01, ϕ=.95)
444+ cvar_seq2 = mc.compute_variation(ξ1=.05, ϕ=.95)
445+ cvar_seq3 = mc.compute_variation(ξ1=.01, ϕ=1.02)
446+ cvar_seq4 = mc.compute_variation(ξ1=.05, ϕ=1.02)
447+ ```
448+
449+ ``` {code-cell} ipython3
450+ print('welfare of optimal c: ', mc.welfare(c_seq))
451+ print('variation 1: ', mc.welfare(cvar_seq1))
452+ print('variation 2:', mc.welfare(cvar_seq2))
453+ print('variation 3: ', mc.welfare(cvar_seq3))
454+ print('variation 4:', mc.welfare(cvar_seq4))
455+ ```
456+
457+ ``` {code-cell} ipython3
458+ plt.plot(range(T+1), c_seq, color='orange', label=r'Optimal $\vec{c}$ ')
459+ plt.plot(range(T+1), cvar_seq1, color='tab:blue', label=r'$\xi_1 = 0.01, \phi = 0.95$')
460+ plt.plot(range(T+1), cvar_seq2, color='tab:blue', ls='-.', label=r'$\xi_1 = 0.05, \phi = 0.95$')
461+ plt.plot(range(T+1), cvar_seq3, color='tab:green', label=r'$\xi_1 = 0.01, \phi = 1.02$')
462+ plt.plot(range(T+1), cvar_seq4, color='tab:green', ls='-.', label=r'$\xi_1 = 0.05, \phi = 1.02$')
463+
464+
465+ plt.legend()
466+ plt.xlabel(r'$t$')
467+ plt.ylabel(r'$c_t$')
468+ plt.show()
469+ ```
470+
471+ ``` {code-cell} ipython3
472+ def welfare_ϕ(mc, ξ1, ϕ):
473+ "Compute welfare of variation sequence for given ϕ, ξ1 with an instance of our model mc"
474+ cvar_seq = mc.compute_variation(ξ1=ξ1, ϕ=ϕ, verbose=0)
475+ return mc.welfare(cvar_seq)
476+
477+ welfare_φ = np.vectorize(welfare_φ)
478+ ξ1_arr = np.linspace(-0.5, 0.5, 20)
479+
480+ plt.plot(ξ1_arr, welfare_φ(mc, ξ1=ξ1_arr , ϕ=1.02))
481+ plt.ylabel('welfare')
482+ plt.xlabel(r'$\xi_1$')
483+ plt.show()
484+ ```
309485
310486``` {code-cell} ipython3
311487
0 commit comments