Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parameterize adjustments to baseline spending levels in reform policy #987

Merged
merged 29 commits into from
Oct 3, 2024
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
cf19eea
add new params for adjusting baseline spending
jdebacker Sep 12, 2024
fea7e07
update default vals
jdebacker Sep 12, 2024
9fc91ce
apply new params in fiscal.py
jdebacker Sep 12, 2024
1738e3f
extrapolate new params
jdebacker Sep 12, 2024
0506667
update SS and TPI algos to apply haircut to baseline spending
jdebacker Sep 12, 2024
be7d5ae
format
jdebacker Sep 12, 2024
45476d6
test updates to get_I_g
jdebacker Sep 12, 2024
36b1e7d
fix issues with baseline spend values
jdebacker Sep 13, 2024
ec982c4
clarify when using baseline values
jdebacker Sep 13, 2024
c1647de
correct args for inner loop
jdebacker Sep 14, 2024
95e495c
just return actual baseline values
jdebacker Sep 14, 2024
7b43966
update calls to basleine in tpi
jdebacker Sep 14, 2024
92529e3
format, p.
jdebacker Sep 15, 2024
af93b30
fix typo
jdebacker Sep 15, 2024
6e665fc
arg for extra ss solver tests
jdebacker Sep 16, 2024
2028762
assign value to TR_ss
jdebacker Sep 16, 2024
bc1acd8
initial I_g for all calles
jdebacker Sep 26, 2024
7a324c3
Merge remote-tracking branch 'upstream/master' into basespend_adj
jdebacker Sep 26, 2024
9c26e4a
Merge remote-tracking branch 'upstream/master' into basespend_adj
jdebacker Sep 27, 2024
92d09fc
describe baseline spending in the docs
jdebacker Sep 28, 2024
c6b582f
update parameters docs
jdebacker Sep 28, 2024
44902ac
format
jdebacker Sep 28, 2024
f429a85
update changelog for new release
jdebacker Sep 29, 2024
7e551de
bump target py versions
jdebacker Sep 29, 2024
394ecc6
update version number
jdebacker Sep 29, 2024
ce11f5f
bump date in changelog
jdebacker Oct 2, 2024
85b9e62
update results for extra run with Kg > 0
jdebacker Oct 2, 2024
0d72a5b
fix get I in non base spend
jdebacker Oct 2, 2024
4137152
Merge remote-tracking branch 'upstream/master' into basespend_adj
jdebacker Oct 2, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,192 changes: 621 additions & 571 deletions docs/book/content/intro/parameters.md

Large diffs are not rendered by default.

11 changes: 11 additions & 0 deletions docs/book/content/theory/government.md
Original file line number Diff line number Diff line change
Expand Up @@ -564,6 +564,17 @@ Total pension spending is the sum of the pension payments to each household in t

Put description of growth-adjusted specification here.

#### Spending in the `reform` simulation

While aggregate spending on $G$, $TR$, and $I_g$ in the baseline simulation are set as fractions of GDP, in the `reform` simulation, these spending amounts can be set in two different ways. The method is controlled by the `baseline_spending` parameter. If `baseline_spending=False`, the behavior of these spending is analgous to that in the baseline simulation; they are set as fractions of GDP, in this case **GDP in the reform simulation**. Thus, with the default assumption of `baseline_spending` it's assumed that spending levels in these three categories are function of GDP in the reform. In this case, users will see that, even with the parameters $\alpha_G$, $\alpha_T$, and $\alpha_I$ are unchanged, the *level* of spending will change in the reform *if* GDP in the reform is different.

With the assumption of `baseline_spending=True`, the level of spending in the reform is held to the level of spending in the baseline. If the user wishes to adjust the level of spending, relative to the baseline level, in the reform, then the parameters `alpha_bs_G`, `alpha_bs_T`, and `alpha_bs_I` can be used to proportionally increase or decrease the levels of spending on $G$, $TR$, and $I_g$ in the reform simulation, relative to the levels in the baseline simulation. Note that the `alpha_bs_*` parameters are time varying, so the proportional change in spending can be different across time. E.g., for government consumption expenditures, we'd have the reform amount of $G$ determined as:

```{math}
G^{reform}_t = \alpha^{BS}_{G,t}G^{baseline}_{t}
```

Note that the budget closure rule (described in Section ref{`SecUnbalGBCcloseRule`}) still takes effect in the case of `baseline_spending=True`. What this means is that the relation described above holds until the period in which the closure rules takes effect. Once the closure rule begins, the path of $G$ (and/or $TR$, depending on the closure rule used) will adjust as determined by the rule to close the government budget in the long run.

(SecUnbalGBCrev)=
## Government Tax Revenue
Expand Down
105 changes: 87 additions & 18 deletions ogcore/SS.py
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,9 @@ def inner_loop(outer_loop_vars, p, client):
scattered_p = client.scatter(p, broadcast=True) if client else p

# unpack variables to pass to function
bssmat, nssmat, r_p, r, w, p_m, Y, BQ, TR, factor = outer_loop_vars
bssmat, nssmat, r_p, r, w, p_m, Y, BQ, TR, Ig_baseline, factor = (
outer_loop_vars
)

p_m = np.array(p_m) # TODO: why is this a list otherwise?
p_i = np.dot(p.io_matrix, p_m)
Expand Down Expand Up @@ -333,7 +335,7 @@ def inner_loop(outer_loop_vars, p, client):
D, D_d, D_f, new_borrowing, _, new_borrowing_f = fiscal.get_D_ss(
r_gov, Y, p
)
I_g = fiscal.get_I_g(Y, p.alpha_I[-1])
I_g = fiscal.get_I_g(Y, Ig_baseline, p, "SS")
K_g = fiscal.get_K_g(0, I_g, p, "SS")

# Find wage rate consistent with open economy interest rate
Expand Down Expand Up @@ -372,7 +374,7 @@ def inner_loop(outer_loop_vars, p, client):
Y_vec[-1] = firm.get_Y(K_vec[-1], K_g, L_vec[-1], p, "SS", -1)
# Find GDP
Y = (p_m * Y_vec).sum()
I_g = fiscal.get_I_g(Y, p.alpha_I[-1])
I_g = fiscal.get_I_g(Y, Ig_baseline, p, "SS")
K_g = fiscal.get_K_g(0, I_g, p, "SS")
if p.zeta_K[-1] == 1.0:
new_r = p.world_int_rate[-1]
Expand Down Expand Up @@ -553,7 +555,20 @@ def inner_loop(outer_loop_vars, p, client):


def SS_solver(
bmat, nmat, r_p, r, w, p_m, Y, BQ, TR, factor, p, client, fsolve_flag=False
bmat,
nmat,
r_p,
r,
w,
p_m,
Y,
BQ,
TR,
Ig_baseline,
factor,
p,
client,
fsolve_flag=False,
):
"""
Solves for the steady state distribution of capital, labor, as well
Expand Down Expand Up @@ -586,18 +601,30 @@ def SS_solver(
if fsolve_flag: # case where already solved via SS_fsolve
maxiter_ss = 1
if p.baseline_spending:
TR_ss = TR
TR_baseline = p.alpha_bs_T[-1] * TR
if not p.budget_balance and not p.baseline_spending:
Y = TR / p.alpha_T[-1]
while (dist > p.mindist_SS) and (iteration < maxiter_ss):
# Solve for the steady state levels of b and n, given w, r,
# Y, BQ, TR, and factor
if p.baseline_spending:
TR = TR_ss
TR = p.alpha_bs_T[-1] * TR_baseline
if not p.budget_balance and not p.baseline_spending:
Y = TR / p.alpha_T[-1]

outer_loop_vars = (bmat, nmat, r_p, r, w, p_m, Y, BQ, TR, factor)
outer_loop_vars = (
bmat,
nmat,
r_p,
r,
w,
p_m,
Y,
BQ,
TR,
Ig_baseline,
factor,
)

(
euler_errors,
Expand Down Expand Up @@ -654,7 +681,7 @@ def SS_solver(
).max()
else:
if p.baseline_spending:
TR = TR_ss
TR = p.alpha_bs_T[-1] * TR_baseline
else:
TR = utils.convex_combo(new_TR, TR, nu_ss)
dist = np.array(
Expand Down Expand Up @@ -695,7 +722,7 @@ def SS_solver(
RM_ss = new_RM
TR_ss = new_TR
Yss = new_Y
I_g_ss = fiscal.get_I_g(Yss, p.alpha_I[-1])
I_g_ss = fiscal.get_I_g(Yss, Ig_baseline, p, "SS")
K_g_ss = fiscal.get_K_g(0, I_g_ss, p, "SS")
Lss = aggr.get_L(nssmat, p, "SS")
Bss = aggr.get_B(bssmat_splus1, p, "SS", False)
Expand All @@ -718,7 +745,7 @@ def SS_solver(
Bss, K_demand_open_ss.sum(), D_d_ss, p.zeta_K[-1]
)
# Yss = firm.get_Y(Kss, K_g_ss, Lss, p, 'SS')
I_g_ss = fiscal.get_I_g(Yss, p.alpha_I[-1])
I_g_ss = fiscal.get_I_g(Yss, Ig_baseline, p, "SS")
K_g_ss = fiscal.get_K_g(0, I_g_ss, p, "SS")
MPKg_vec = np.zeros(p.M)
for m in range(p.M):
Expand Down Expand Up @@ -1057,7 +1084,7 @@ def SS_fsolve(guesses, *args):
implied outer loop variables

"""
(bssmat, nssmat, TR_ss, factor_ss, p, client) = args
(bssmat, nssmat, TR_ss, Ig_baseline, factor_ss, p, client) = args

# Rename the inputs
r_p = guesses[0]
Expand All @@ -1078,7 +1105,19 @@ def SS_fsolve(guesses, *args):
if not p.budget_balance and not p.baseline_spending:
Y = TR / p.alpha_T[-1]

outer_loop_vars = (bssmat, nssmat, r_p, r, w, p_m, Y, BQ, TR, factor)
outer_loop_vars = (
bssmat,
nssmat,
r_p,
r,
w,
p_m,
Y,
BQ,
TR,
Ig_baseline,
factor,
)

# Solve for the steady state levels of b and n, given w, r, TR and
# factor
Expand Down Expand Up @@ -1236,7 +1275,15 @@ def run_SS(p, client=None):
Yguess = TRguess / p.alpha_T[-1]
factorguess = p.initial_guess_factor_SS
BQguess = aggr.get_BQ(rguess, b_guess, None, p, "SS", False)
ss_params_baseline = (b_guess, n_guess, None, None, p, client)
ss_params_baseline = (
b_guess,
n_guess,
None,
None,
None,
p,
client,
)
if p.use_zeta:
BQguess = 0.12231465279007188
guesses = (
Expand Down Expand Up @@ -1285,6 +1332,7 @@ def run_SS(p, client=None):
Yss,
BQss,
TR_ss,
None,
factor_ss,
p,
client,
Expand Down Expand Up @@ -1364,21 +1412,30 @@ def run_SS(p, client=None):
if p.use_zeta:
BQguess = 0.12231465279007188
if p.baseline_spending:
TR_ss = TRguess
ss_params_reform = (b_guess, n_guess, TR_ss, factor, p, client)
TR_baseline = TRguess
Ig_baseline = ss_solutions["I_g_ss"]
ss_params_reform = (
b_guess,
n_guess,
TR_baseline,
Ig_baseline,
factor,
p,
client,
)
if p.use_zeta:
guesses = (
[r_p_guess, rguess, wguess]
+ list(p_m_guess)
+ [Yguess, BQguess, TR_ss]
+ [Yguess, BQguess, TR_baseline]
)
else:
guesses = (
[r_p_guess, rguess, wguess]
+ list(p_m_guess)
+ [Yguess]
+ list(BQguess)
+ [TR_ss]
+ [TR_baseline]
)
sol = opt.root(
SS_fsolve,
Expand All @@ -1393,8 +1450,17 @@ def run_SS(p, client=None):
p_m_ss = sol.x[3 : 3 + p.M]
Yss = sol.x[3 + p.M]
BQss = sol.x[3 + p.M + 1 : -1]
TR_ss = sol.x[-1]
else:
ss_params_reform = (b_guess, n_guess, None, factor, p, client)
ss_params_reform = (
b_guess,
n_guess,
None,
None,
factor,
p,
client,
)
if p.use_zeta:
guesses = (
[r_p_guess, rguess, wguess]
Expand Down Expand Up @@ -1435,6 +1501,8 @@ def run_SS(p, client=None):
# Return SS values of variables
fsolve_flag = True
# Return SS values of variables
if not p.baseline_spending:
Ig_baseline = None
output = SS_solver(
b_guess,
n_guess,
Expand All @@ -1445,6 +1513,7 @@ def run_SS(p, client=None):
Yss,
BQss,
TR_ss,
Ig_baseline,
factor,
p,
client,
Expand Down
28 changes: 17 additions & 11 deletions ogcore/TPI.py
Original file line number Diff line number Diff line change
Expand Up @@ -623,16 +623,6 @@ def run_TPI(p, client=None):
Y = np.zeros_like(K)
Y[: p.T] = firm.get_Y(K[: p.T], K_g[: p.T], L[: p.T], p, "TPI")
Y[p.T :] = ss_vars["Yss"]
I_g = np.ones_like(Y) * ss_vars["I_g_ss"]
if p.baseline_spending:
I_g[: p.T] = Ig_baseline[: p.T]
else:
I_g = fiscal.get_I_g(Y[: p.T], p.alpha_I[: p.T])
if p.baseline:
K_g0 = p.initial_Kg_ratio * Y[0]
else:
K_g0 = Kg0_baseline
K_g = fiscal.get_K_g(K_g0, I_g, p, "TPI")
# path for industry specific aggregates
K_vec_init = np.ones((p.T + p.S, p.M)) * ss_vars["K_vec_ss"].reshape(
1, p.M
Expand Down Expand Up @@ -697,20 +687,36 @@ def run_TPI(p, client=None):
D = np.zeros(p.T + p.S)
D_d = np.zeros(p.T + p.S)
D_f = np.zeros(p.T + p.S)
I_g = np.ones_like(Y) * ss_vars["I_g_ss"]
else:
if p.baseline_spending:
# Will set to TRbaseline here, but will be updated in TPI loop
# with call to fiscal.get_TR
TR = np.concatenate(
(TRbaseline[: p.T], np.ones(p.S) * ss_vars["TR_ss"])
)
# Will set to Ig_baseline here, but will be updated in TPI loop
# with call to fiscal.get_I_g
I_g = np.concatenate(
(Ig_baseline[: p.T], np.ones(p.S) * ss_vars["I_g_ss"])
)
# Will set to Gbaseline here, but will be updated in TPI loop
# with call to fiscal.D_G_path, which also does closure rule
G = np.concatenate(
(Gbaseline[: p.T], np.ones(p.S) * ss_vars["Gss"])
)
else:
TR = p.alpha_T * Y
G = np.ones(p.T + p.S) * ss_vars["Gss"]
I_g = np.ones(p.T + p.S) * ss_vars["I_g_ss"]
D = np.ones(p.T + p.S) * ss_vars["Dss"]
D_d = D * ss_vars["D_d_ss"] / ss_vars["Dss"]
D_f = D * ss_vars["D_f_ss"] / ss_vars["Dss"]
if p.baseline:
K_g0 = p.initial_Kg_ratio * Y[0]
else:
K_g0 = Kg0_baseline
K_g = fiscal.get_K_g(K_g0, I_g, p, "TPI")
total_tax_revenue = np.ones(p.T + p.S) * ss_vars["total_tax_revenue"]

# Compute other interest rates
Expand Down Expand Up @@ -993,7 +999,7 @@ def run_TPI(p, client=None):
B[: p.T], K_demand_open_vec.sum(-1), D_d[: p.T], p.zeta_K[: p.T]
)
if not p.baseline_spending:
I_g = fiscal.get_I_g(Y[: p.T], p.alpha_I[: p.T])
I_g = fiscal.get_I_g(Y[: p.T], None, p, "TPI")
if p.baseline:
K_g0 = p.initial_Kg_ratio * Y[0]
K_g = fiscal.get_K_g(K_g0, I_g, p, "TPI")
Expand Down
Loading
Loading