Skip to content

Commit 264724e

Browse files
authored
[opt_tax_recur+amss] migrate updates from lecture-python-advanced (#76)
* [opt_tax_recur] migrate updates from lecture-python-advanced * update lecture code files * [migrate] amss,amss2,amss3 updates from lecture-python-advanced * [amss] add jupyter-book tags for controlling output and collapse blocks * [amss2] add jupyter-book tags for controlling output and collapse blocks
1 parent a2e6522 commit 264724e

File tree

14 files changed

+1359
-806
lines changed

14 files changed

+1359
-806
lines changed

lectures/_static/lecture_specific/amss/recursive_allocation.py

Lines changed: 211 additions & 297 deletions
Large diffs are not rendered by default.
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import numpy as np
2+
3+
4+
class CRRAutility:
5+
6+
def __init__(self,
7+
β=0.9,
8+
σ=2,
9+
γ=2,
10+
π=0.5*np.ones((2, 2)),
11+
G=np.array([0.1, 0.2]),
12+
Θ=np.ones(2),
13+
transfers=False):
14+
15+
self.β, self.σ, self.γ = β, σ, γ
16+
self.π, self.G, self.Θ, self.transfers = π, G, Θ, transfers
17+
18+
# Utility function
19+
def U(self, c, n):
20+
σ = self.σ
21+
if σ == 1.:
22+
U = np.log(c)
23+
else:
24+
U = (c**(1 - σ) - 1) / (1 - σ)
25+
return U - n**(1 + self.γ) / (1 + self.γ)
26+
27+
# Derivatives of utility function
28+
def Uc(self, c, n):
29+
return c**(-self.σ)
30+
31+
def Ucc(self, c, n):
32+
return -self.σ * c**(-self.σ - 1)
33+
34+
def Un(self, c, n):
35+
return -n**self.γ
36+
37+
def Unn(self, c, n):
38+
return -self.γ * n**(self.γ - 1)
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import numpy as np
2+
3+
class LogUtility:
4+
5+
def __init__(self,
6+
β=0.9,
7+
ψ=0.69,
8+
π=0.5*np.ones((2, 2)),
9+
G=np.array([0.1, 0.2]),
10+
Θ=np.ones(2),
11+
transfers=False):
12+
13+
self.β, self.ψ, self.π = β, ψ, π
14+
self.G, self.Θ, self.transfers = G, Θ, transfers
15+
16+
# Utility function
17+
def U(self, c, n):
18+
return np.log(c) + self.ψ * np.log(1 - n)
19+
20+
# Derivatives of utility function
21+
def Uc(self, c, n):
22+
return 1 / c
23+
24+
def Ucc(self, c, n):
25+
return -c**(-2)
26+
27+
def Un(self, c, n):
28+
return -self.ψ / (1 - n)
29+
30+
def Unn(self, c, n):
31+
return -self.ψ / (1 - n)**2
Lines changed: 298 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,298 @@
1+
import numpy as np
2+
from scipy.optimize import fmin_slsqp
3+
from scipy.optimize import root
4+
from quantecon import MarkovChain
5+
6+
7+
class RecursiveAllocationAMSS:
8+
9+
def __init__(self, model, μgrid, tol_diff=1e-7, tol=1e-7):
10+
11+
self.β, self.π, self.G = model.β, model.π, model.G
12+
self.mc, self.S = MarkovChain(self.π), len(model.π) # Number of states
13+
self.Θ, self.model, self.μgrid = model.Θ, model, μgrid
14+
self.tol_diff, self.tol = tol_diff, tol
15+
16+
# Find the first best allocation
17+
self.solve_time1_bellman()
18+
self.T.time_0 = True # Bellman equation now solves time 0 problem
19+
20+
def solve_time1_bellman(self):
21+
'''
22+
Solve the time 1 Bellman equation for calibration model and
23+
initial grid μgrid0
24+
'''
25+
model, μgrid0 = self.model, self.μgrid
26+
π = model.π
27+
S = len(model.π)
28+
29+
# First get initial fit from Lucas Stokey solution.
30+
# Need to change things to be ex ante
31+
pp = SequentialAllocation(model)
32+
interp = interpolator_factory(2, None)
33+
34+
def incomplete_allocation(μ_, s_):
35+
c, n, x, V = pp.time1_value(μ_)
36+
return c, n, π[s_] @ x, π[s_] @ V
37+
cf, nf, xgrid, Vf, xprimef = [], [], [], [], []
38+
for s_ in range(S):
39+
c, n, x, V = zip(*map(lambda μ: incomplete_allocation(μ, s_), μgrid0))
40+
c, n = np.vstack(c).T, np.vstack(n).T
41+
x, V = np.hstack(x), np.hstack(V)
42+
xprimes = np.vstack([x] * S)
43+
cf.append(interp(x, c))
44+
nf.append(interp(x, n))
45+
Vf.append(interp(x, V))
46+
xgrid.append(x)
47+
xprimef.append(interp(x, xprimes))
48+
cf, nf, xprimef = fun_vstack(cf), fun_vstack(nf), fun_vstack(xprimef)
49+
Vf = fun_hstack(Vf)
50+
policies = [cf, nf, xprimef]
51+
52+
# Create xgrid
53+
x = np.vstack(xgrid).T
54+
xbar = [x.min(0).max(), x.max(0).min()]
55+
xgrid = np.linspace(xbar[0], xbar[1], len(μgrid0))
56+
self.xgrid = xgrid
57+
58+
# Now iterate on Bellman equation
59+
T = BellmanEquation(model, xgrid, policies, tol=self.tol)
60+
diff = 1
61+
while diff > self.tol_diff:
62+
PF = T(Vf)
63+
64+
Vfnew, policies = self.fit_policy_function(PF)
65+
diff = np.abs((Vf(xgrid) - Vfnew(xgrid)) / Vf(xgrid)).max()
66+
67+
print(diff)
68+
Vf = Vfnew
69+
70+
# Store value function policies and Bellman Equations
71+
self.Vf = Vf
72+
self.policies = policies
73+
self.T = T
74+
75+
def fit_policy_function(self, PF):
76+
'''
77+
Fits the policy functions
78+
'''
79+
S, xgrid = len(self.π), self.xgrid
80+
interp = interpolator_factory(3, 0)
81+
cf, nf, xprimef, Tf, Vf = [], [], [], [], []
82+
for s_ in range(S):
83+
PFvec = np.vstack([PF(x, s_) for x in self.xgrid]).T
84+
Vf.append(interp(xgrid, PFvec[0, :]))
85+
cf.append(interp(xgrid, PFvec[1:1 + S]))
86+
nf.append(interp(xgrid, PFvec[1 + S:1 + 2 * S]))
87+
xprimef.append(interp(xgrid, PFvec[1 + 2 * S:1 + 3 * S]))
88+
Tf.append(interp(xgrid, PFvec[1 + 3 * S:]))
89+
policies = fun_vstack(cf), fun_vstack(
90+
nf), fun_vstack(xprimef), fun_vstack(Tf)
91+
Vf = fun_hstack(Vf)
92+
return Vf, policies
93+
94+
def Τ(self, c, n):
95+
'''
96+
Computes Τ given c and n
97+
'''
98+
model = self.model
99+
Uc, Un = model.Uc(c, n), model.Un(c, n)
100+
101+
return 1 + Un / (self.Θ * Uc)
102+
103+
def time0_allocation(self, B_, s0):
104+
'''
105+
Finds the optimal allocation given initial government debt B_ and
106+
state s_0
107+
'''
108+
PF = self.T(self.Vf)
109+
z0 = PF(B_, s0)
110+
c0, n0, xprime0, T0 = z0[1:]
111+
return c0, n0, xprime0, T0
112+
113+
def simulate(self, B_, s_0, T, sHist=None):
114+
'''
115+
Simulates planners policies for T periods
116+
'''
117+
model, π = self.model, self.π
118+
Uc = model.Uc
119+
cf, nf, xprimef, Tf = self.policies
120+
121+
if sHist is None:
122+
sHist = simulate_markov(π, s_0, T)
123+
124+
cHist, nHist, Bhist, xHist, ΤHist, THist, μHist = np.zeros((7, T))
125+
# Time 0
126+
cHist[0], nHist[0], xHist[0], THist[0] = self.time0_allocation(B_, s_0)
127+
ΤHist[0] = self.Τ(cHist[0], nHist[0])[s_0]
128+
Bhist[0] = B_
129+
μHist[0] = self.Vf[s_0](xHist[0])
130+
131+
# Time 1 onward
132+
for t in range(1, T):
133+
s_, x, s = sHist[t - 1], xHist[t - 1], sHist[t]
134+
c, n, xprime, T = cf[s_, :](x), nf[s_, :](
135+
x), xprimef[s_, :](x), Tf[s_, :](x)
136+
137+
Τ = self.Τ(c, n)[s]
138+
u_c = Uc(c, n)
139+
Eu_c = π[s_, :] @ u_c
140+
141+
μHist[t] = self.Vf[s](xprime[s])
142+
143+
cHist[t], nHist[t], Bhist[t], ΤHist[t] = c[s], n[s], x / Eu_c, Τ
144+
xHist[t], THist[t] = xprime[s], T[s]
145+
return np.array([cHist, nHist, Bhist, ΤHist, THist, μHist, sHist, xHist])
146+
147+
148+
class BellmanEquation:
149+
'''
150+
Bellman equation for the continuation of the Lucas-Stokey Problem
151+
'''
152+
153+
def __init__(self, model, xgrid, policies0, tol, maxiter=1000):
154+
155+
self.β, self.π, self.G = model.β, model.π, model.G
156+
self.S = len(model.π) # Number of states
157+
self.Θ, self.model, self.tol = model.Θ, model, tol
158+
self.maxiter = maxiter
159+
160+
self.xbar = [min(xgrid), max(xgrid)]
161+
self.time_0 = False
162+
163+
self.z0 = {}
164+
cf, nf, xprimef = policies0
165+
166+
for s_ in range(self.S):
167+
for x in xgrid:
168+
self.z0[x, s_] = np.hstack([cf[s_, :](x),
169+
nf[s_, :](x),
170+
xprimef[s_, :](x),
171+
np.zeros(self.S)])
172+
173+
self.find_first_best()
174+
175+
def find_first_best(self):
176+
'''
177+
Find the first best allocation
178+
'''
179+
model = self.model
180+
S, Θ, Uc, Un, G = self.S, self.Θ, model.Uc, model.Un, self.G
181+
182+
def res(z):
183+
c = z[:S]
184+
n = z[S:]
185+
return np.hstack([Θ * Uc(c, n) + Un(c, n), Θ * n - c - G])
186+
187+
res = root(res, 0.5 * np.ones(2 * S))
188+
if not res.success:
189+
raise Exception('Could not find first best')
190+
191+
self.cFB = res.x[:S]
192+
self.nFB = res.x[S:]
193+
IFB = Uc(self.cFB, self.nFB) * self.cFB + \
194+
Un(self.cFB, self.nFB) * self.nFB
195+
196+
self.xFB = np.linalg.solve(np.eye(S) - self.β * self.π, IFB)
197+
198+
self.zFB = {}
199+
for s in range(S):
200+
self.zFB[s] = np.hstack(
201+
[self.cFB[s], self.nFB[s], self.π[s] @ self.xFB, 0.])
202+
203+
def __call__(self, Vf):
204+
'''
205+
Given continuation value function next period return value function this
206+
period return T(V) and optimal policies
207+
'''
208+
if not self.time_0:
209+
def PF(x, s): return self.get_policies_time1(x, s, Vf)
210+
else:
211+
def PF(B_, s0): return self.get_policies_time0(B_, s0, Vf)
212+
return PF
213+
214+
def get_policies_time1(self, x, s_, Vf):
215+
'''
216+
Finds the optimal policies
217+
'''
218+
model, β, Θ, G, S, π = self.model, self.β, self.Θ, self.G, self.S, self.π
219+
U, Uc, Un = model.U, model.Uc, model.Un
220+
221+
def objf(z):
222+
c, n, xprime = z[:S], z[S:2 * S], z[2 * S:3 * S]
223+
224+
Vprime = np.empty(S)
225+
for s in range(S):
226+
Vprime[s] = Vf[s](xprime[s])
227+
228+
return -π[s_] @ (U(c, n) + β * Vprime)
229+
230+
def objf_prime(x):
231+
232+
epsilon = 1e-7
233+
x0 = np.asfarray(x)
234+
f0 = np.atleast_1d(objf(x0))
235+
jac = np.zeros([len(x0), len(f0)])
236+
dx = np.zeros(len(x0))
237+
for i in range(len(x0)):
238+
dx[i] = epsilon
239+
jac[i] = (objf(x0+dx) - f0)/epsilon
240+
dx[i] = 0.0
241+
242+
return jac.transpose()
243+
244+
def cons(z):
245+
c, n, xprime, T = z[:S], z[S:2 * S], z[2 * S:3 * S], z[3 * S:]
246+
u_c = Uc(c, n)
247+
Eu_c = π[s_] @ u_c
248+
return np.hstack([
249+
x * u_c / Eu_c - u_c * (c - T) - Un(c, n) * n - β * xprime,
250+
Θ * n - c - G])
251+
252+
if model.transfers:
253+
bounds = [(0., 100)] * S + [(0., 100)] * S + \
254+
[self.xbar] * S + [(0., 100.)] * S
255+
else:
256+
bounds = [(0., 100)] * S + [(0., 100)] * S + \
257+
[self.xbar] * S + [(0., 0.)] * S
258+
out, fx, _, imode, smode = fmin_slsqp(objf, self.z0[x, s_],
259+
f_eqcons=cons, bounds=bounds,
260+
fprime=objf_prime, full_output=True,
261+
iprint=0, acc=self.tol, iter=self.maxiter)
262+
263+
if imode > 0:
264+
raise Exception(smode)
265+
266+
self.z0[x, s_] = out
267+
return np.hstack([-fx, out])
268+
269+
def get_policies_time0(self, B_, s0, Vf):
270+
'''
271+
Finds the optimal policies
272+
'''
273+
model, β, Θ, G = self.model, self.β, self.Θ, self.G
274+
U, Uc, Un = model.U, model.Uc, model.Un
275+
276+
def objf(z):
277+
c, n, xprime = z[:-1]
278+
279+
return -(U(c, n) + β * Vf[s0](xprime))
280+
281+
def cons(z):
282+
c, n, xprime, T = z
283+
return np.hstack([
284+
-Uc(c, n) * (c - B_ - T) - Un(c, n) * n - β * xprime,
285+
(Θ * n - c - G)[s0]])
286+
287+
if model.transfers:
288+
bounds = [(0., 100), (0., 100), self.xbar, (0., 100.)]
289+
else:
290+
bounds = [(0., 100), (0., 100), self.xbar, (0., 0.)]
291+
out, fx, _, imode, smode = fmin_slsqp(objf, self.zFB[s0], f_eqcons=cons,
292+
bounds=bounds, full_output=True,
293+
iprint=0)
294+
295+
if imode > 0:
296+
raise Exception(smode)
297+
298+
return np.hstack([-fx, out])

0 commit comments

Comments
 (0)