From b13c9134556cc468ce0859aadbd14c60d1b39100 Mon Sep 17 00:00:00 2001 From: Humphrey Yang Date: Wed, 24 Dec 2025 14:34:59 +1100 Subject: [PATCH 01/17] updates --- lectures/_toc.yml | 1 + lectures/gorman_heterogeneous_households.md | 1676 +++++++++++++++++++ 2 files changed, 1677 insertions(+) create mode 100644 lectures/gorman_heterogeneous_households.md diff --git a/lectures/_toc.yml b/lectures/_toc.yml index 497aa68f..4584925c 100644 --- a/lectures/_toc.yml +++ b/lectures/_toc.yml @@ -34,6 +34,7 @@ parts: - file: lucas_asset_pricing_dles - file: irfs_in_hall_model - file: permanent_income_dles + - file: gorman_heterogeneous_households - file: rosen_schooling_model - file: cattle_cycles - file: hs_invertibility_example diff --git a/lectures/gorman_heterogeneous_households.md b/lectures/gorman_heterogeneous_households.md new file mode 100644 index 00000000..9f0c8041 --- /dev/null +++ b/lectures/gorman_heterogeneous_households.md @@ -0,0 +1,1676 @@ +--- +jupytext: + text_representation: + extension: .md + format_name: myst + format_version: 0.13 + jupytext_version: 1.17.1 +kernelspec: + display_name: Python 3 (ipykernel) + language: python + name: python3 +--- + +(gorman_heterogeneous_households)= +```{raw} html +
+ + QuantEcon + +
+``` + +```{index} single: python +``` + +# Gorman Heterogeneous Households and Limited Markets + +This lecture implements the Gorman heterogeneous-household economy in Chapter 12 of {cite:t}`HS2013` using the `quantecon.DLE` class. + +It complements [](hs_recursive_models), [](growth_in_dles), and [](irfs_in_hall_model) by focusing on how to recover household allocations and portfolios from an aggregate DLE solution. + +The headline result is that a complete-markets allocation can be implemented with a mutual fund and a one-period bond when Gorman aggregation holds. + +This lecture does three things. + +- It solves the aggregate planner problem with `DLE`. +- It computes household weights and deviation terms. +- It simulates a limited-markets portfolio strategy that replicates the Arrow–Debreu allocation. + +In addition to what's in Anaconda, this lecture uses the quantecon library. + +```{code-cell} ipython3 +:tags: [hide-output] + +!pip install --upgrade quantecon +``` + +We make the following imports. + +```{code-cell} ipython3 +import numpy as np +from scipy.linalg import solve_discrete_are +from quantecon import DLE +from quantecon._lqcontrol import LQ +import matplotlib.pyplot as plt +``` + +## Overview + +Gorman aggregation lets us solve heterogeneous-household economies in two steps. + +First, solve a representative-agent linear-quadratic planning problem to obtain aggregate allocations, shadow prices, and the closed-loop law of motion. + +Second, recover household-level allocations via a wealth-share rule with a household-specific deviation term. + +The idea behind Gorman aggregation is beautifully simple yet powerful. + +In a general heterogeneous-consumer economy, the set of Pareto optimal allocations depends on how utility is distributed across consumers. + +If we change the aggregate endowment, the new utility possibility frontier may cross the old one, meaning there is no way to rank aggregate allocations independently of distribution. + +Gorman's conditions eliminate this problem: they ensure that all consumers have parallel Engel curves, so the coefficient on wealth in the demand function is the same for everyone. + +This lets us determine aggregate allocations and prices before worrying about how to divide consumption among households. + +With the help of this powerful result, we proceed in three steps in this lecture: + +1. Solve the planner's problem and compute selection matrices that map the aggregate state into allocations and prices. +2. Compute household-specific policies and the Gorman sharing rule. +3. Implement the same Arrow-Debreu allocation using only a mutual fund (aggregate stock) and a one-period bond. + +We then simulate examples with two and many households. + +### Notation + +Time is discrete, $t = 0,1,2,\dots$. + +Households are indexed by $j$ and we use $N$ for the number of simulated households. + +#### Exogenous state + +The exogenous state $z_t$ follows a first-order vector autoregression + +$$ +z_{t+1} = A_{22} z_t + C_2 w_{t+1}, +$$ + +where $A_{22}$ governs persistence and $C_2$ maps i.i.d. shocks $w_{t+1}$ into the state. + +The vector $z_t$ typically contains three types of components. + +1. Constant: The first element is set to 1 and remains constant. + +2. Aggregate shocks: Components with persistent dynamics (nonzero entries in $A_{22}$) that affect all households. + + - In the examples in {ref}`gorman_twohh`, an AR(2) process drives aggregate endowment fluctuations. + +3. Idiosyncratic shocks: Components with transitory or zero persistence that enter individual endowments with loadings summing to zero across households. + + - These generate cross-sectional heterogeneity while preserving aggregate resource constraints. + +The selection matrices $U_b$ and $U_d$ pick out which components of $z_t$ affect +household preferences (bliss points) and endowments. + +#### Aggregate planner state + +The aggregate planner state stacks lagged endogenous stocks and current exogenous variables: + +$$ +x_t = [h_{t-1}^\top, k_{t-1}^\top, z_t^\top]^\top. +$$ + +Here $h_{t-1}$ is the lagged household durable stock (habits or durables affecting utility), $k_{t-1}$ is lagged physical capital, and $z_t$ is the current exogenous state. + +Together, $x_t$ contains everything the planner needs to make decisions at time $t$. + +#### Aggregates + +Aggregates are economy-wide totals summed across households: consumption $c_t$, investment $i_t$, capital $k_t$, household stock $h_t$, service flow $s_t$, intermediate good $g_t$, bliss $b_t$, and endowment $d_t$. + +#### Gorman weight + +The Gorman weight $\mu_j := u_j/u_a$ is household $j$'s wealth share, where $u_j$ is household $j$'s marginal utility of wealth and $u_a$ is the aggregate. + +The weights sum to one: $\sum_j \mu_j = 1$. + +Under the Gorman sharing rule, household consumption is + +$$ +c_{jt} = \mu_j c_t + \tilde{\chi}_{jt}. +$$ + +The proportional term $\mu_j c_t$ is household $j$'s share of aggregate consumption. + +The deviation term $\tilde{\chi}_{jt}$ captures how household $j$'s consumption differs from its proportional share due to preference and endowment heterogeneity. + +These deviations sum to zero across households: $\sum_j \tilde{\chi}_{jt} = 0$. + +#### Technologies + +$$ +\Phi_c c_t + \Phi_g g_t + \Phi_i i_t = \Gamma k_{t-1} + d_t, \quad +k_t = \Delta_k k_{t-1} + \Theta_k i_t, +$$ + +and + +$$ +h_t = \Delta_h h_{t-1} + \Theta_h c_t, \quad +s_t = \Lambda h_{t-1} + \Pi_h c_t, \quad +b_t = U_b z_t, \quad +d_t = U_d z_t. +$$ + +Selection matrices such as $S_c, S_k, \ldots$ satisfy $c_t = S_c x_t$. + +Shadow-price mappings $M_c, M_k, \ldots$ are used to value streams and recover equilibrium prices. + +### The individual household problem + +Consider an economy with $J$ consumers indexed by $j = 1, 2, \ldots, J$. + +Consumers differ in preferences and endowments but share a common information set. + +Consumer $j$ maximizes + +$$ +-\frac{1}{2} \mathbb{E}_0 \sum_{t=0}^\infty \beta^t \left[(s_{jt} - b_{jt})^\top (s_{jt} - b_{jt}) + \ell_{jt}^\top \ell_{jt}\right] +$$ (eq:hh_objective) + +subject to the household service technology + +$$ +\begin{aligned} +s_{jt} &= \Lambda h_{j,t-1} + \Pi_h c_{jt}, \\ +h_{jt} &= \Delta_h h_{j,t-1} + \Theta_h c_{jt}, +\end{aligned} +$$ (eq:hh_service_tech) + +and the intertemporal budget constraint + +$$ +\mathbb{E}_0 \sum_{t=0}^\infty \beta^t p_{0t} \cdot c_{jt} += \mathbb{E}_0 \sum_{t=0}^\infty \beta^t (w_{t0} \ell_{jt} + \alpha_{0t} \cdot d_{jt}) + v_0 \cdot k_{j,-1}, +$$ (eq:hh_budget) + +where $b_{jt} = U_b^j z_t$ is household $j$'s preference shock, $d_{jt} = U_d^j z_t$ is household $j$'s endowment stream, $h_{j,-1}$ and $k_{j,-1}$ are given initial household stocks, and $\ell_{jt}$ is household labor supply. + +Prices $(p_{0t}, w_{t0}, \alpha_{0t}, v_0)$ are determined in equilibrium. + +Three key features structure the example in this lecture: + +1. All households share the same technology matrices $(\Lambda, \Pi_h, \Delta_h, \Theta_h)$. + +2. Heterogeneity enters only through household-specific preference and endowment parameters $(U_b^j, U_d^j, h_{j,-1}, k_{j,-1})$. + +3. All households observe the same aggregate information $\mathcal{J}_t = [w_t, x_0]$. + +These restrictions enable Gorman aggregation by ensuring that household demands are affine in wealth. + +### From individual problems to the aggregate problem + +The Gorman aggregation conditions establish a powerful equivalence: the competitive equilibrium allocation solves a social planner's problem with a specific set of Pareto weights. + +We can solve for aggregate quantities by maximizing a weighted sum of household utilities rather than solving each household's problem separately. + +Equilibrium prices emerge naturally as the shadow prices from the planner's problem. + +When preferences satisfy Gorman's restrictions, the equilibrium allocation solves a planning problem with Pareto weights given by the household wealth multipliers $\mu_{0j}^w$. + +The problem decomposes into two steps: + +1. In the aggregate step, we solve the planner's problem with a representative consumer by summing across all households. + +2. In the allocation step, we use the sharing rule to distribute aggregate consumption to individual households based on their wealth shares and preference shocks. + +### The aggregate planning problem + +Summing the household budget constraints across $j = 1,\ldots,J$ gives + +$$ +\mathbb{E}_0 \sum_{t=0}^\infty \beta^t p_{0t} \cdot \left(\sum_j c_{jt}\right) += \mathbb{E}_0 \sum_{t=0}^\infty \beta^t \left(w_{t0} \sum_j \ell_{jt} + \alpha_{0t} \cdot \sum_j d_{jt}\right) + v_0 \cdot \sum_j k_{j,-1}. +$$ (eq:agg_budget_sum) + +In equilibrium, aggregate consumption $c_t = \sum_j c_{jt}$ must satisfy the economy's resource constraint + +$$ +\Phi_c c_t + \Phi_g g_t + \Phi_i i_t = \Gamma k_{t-1} + d_t, +$$ (eq:agg_resource_constraint) + +where aggregate endowment is $d_t = \sum_j d_{jt} = U_d z_t$ with $U_d = \sum_j U_d^j$. + +We construct the representative consumer's preferences by summing + +$$ +h_{-1} = \sum_j h_{j,-1}, \quad +k_{-1} = \sum_j k_{j,-1}, \quad +U_b = \sum_j U_b^j, \quad +U_d = \sum_j U_d^j. +$$ (eq:agg_preference_aggregates) + +With these aggregates, the planner maximizes + +$$ +-\frac{1}{2} \mathbb{E}_0 \sum_{t=0}^\infty \beta^t +\left[(s_t - b_t)^\top(s_t - b_t) + g_t^\top g_t\right] +$$ (eq:planner_objective) + +subject to technology constraints + +$$ +\begin{aligned} +\Phi_c c_t + \Phi_g g_t + \Phi_i i_t &= \Gamma k_{t-1} + d_t, \\ +k_t &= \Delta_k k_{t-1} + \Theta_k i_t, \\ +h_t &= \Delta_h h_{t-1} + \Theta_h c_t, \\ +s_t &= \Lambda h_{t-1} + \Pi_h c_t, +\end{aligned} +$$ (eq:planner_constraints) + +and exogenous processes + +$$ +z_{t+1} = A_{22} z_t + C_2 w_{t+1}, \quad +b_t = U_b z_t, \quad +d_t = U_d z_t. +$$ (eq:exogenous_process) + +Solving this aggregate planning problem yields aggregate allocations $(c_t, i_t, k_t, h_t, s_t, g_t)$ as functions of the aggregate state $x_t$. + +The first-order conditions give shadow prices $(M^c_t, M^k_t, M^h_t, M^s_t)$ associated with each constraint. + +These shadow prices correspond to competitive equilibrium prices. + +## Helper routines + +We repeatedly use matrix sums of the form $\sum_{t \ge 0} A_1^t B_1 B_2^\top (A_2^\top)^t$. + +The following helper computes this sum with a doubling algorithm. + +```{code-cell} ipython3 +def doublej2(A1, B1, A2, B2, tol=1e-15, max_iter=10_000): + r""" + Compute V = Σ_{t=0}^∞ A1^t B1 B2' (A2')^t via a doubling algorithm. + """ + A1 = np.asarray(A1, dtype=float) + A2 = np.asarray(A2, dtype=float) + B1 = np.asarray(B1, dtype=float) + B2 = np.asarray(B2, dtype=float) + + α1, α2 = A1.copy(), A2.copy() + V = B1 @ B2.T + diff, it = np.inf, 0 + + while diff > tol and it < max_iter: + α1_next = α1 @ α1 + α2_next = α2 @ α2 + V_next = V + α1 @ V @ α2.T + + diff = np.max(np.abs(V_next - V)) + α1, α2, V = α1_next, α2_next, V_next + it += 1 + + return V +``` + +## Household allocations + +Under the Gorman sharing rule, household $j$'s consumption is + +$$ +c_{jt} = \mu_j c_t + \tilde{\chi}_{jt}, \qquad \mu_j := u_j/u_a. +$$ (eq:sharing_rule) + +The proportional term $\mu_j c_t$ is household $j$'s share of aggregate consumption. + +The deviation term $\tilde{\chi}_{jt}$ captures deviations driven by preference and endowment heterogeneity. + +### Algorithm for computing $\mu_j$ and $\tilde{\chi}_{jt}$ + +Computing household allocations requires three steps. + +**Step 1: Solve the household deviation problem.** + +The "deviation" consumption $\tilde{\chi}_{jt}$ satisfies the inverse canonical representation + +$$ +\begin{aligned} +\tilde{\chi}_{jt} &= -\Pi_h^{-1} \Lambda \tilde{\eta}_{j,t-1} + \Pi_h^{-1} \tilde{b}_{jt}, \\ +\tilde{\eta}_{jt} &= (\Delta_h - \Theta_h \Pi_h^{-1} \Lambda) \tilde{\eta}_{j,t-1} + \Theta_h \Pi_h^{-1} \tilde{b}_{jt}, +\end{aligned} +$$ + +where $\tilde{b}_{jt} = b_{jt} - \mu_j b_t$ is the deviation of household $j$'s bliss point from its share of the aggregate, and $\tilde{\eta}_{j,-1} = h_{j,-1} - \mu_j h_{-1}$. + +This recursion comes from inverting the household service technology. + +When preferences include durables or habits ($\Lambda \neq 0$), the deviation consumption depends on the lagged deviation state $\tilde{\eta}_{j,t-1}$. + +The code solves this as a linear-quadratic control problem using a scaling trick: multiplying the transition matrices by $\sqrt{\beta}$ converts the discounted problem into an undiscounted one that can be solved with a standard discrete algebraic Riccati equation. + +**Step 2: Compute present values.** + +The Gorman weight $\mu_j$ is determined by the household's budget constraint. + +We compute four present values: + +- $W_d$: present value of household $j$'s endowment stream $\{d_{jt}\}$ +- $W_k$: value of household $j$'s initial capital $k_{j,-1}$ +- $W_{c1}$: present value of the "unit" consumption stream (what it costs to consume $c_t$) +- $W_{c2}$: present value of the deviation consumption stream $\{\tilde{\chi}_{jt}\}$ + +Each present value is computed using `doublej2` to sum infinite series of the form $\sum_{t \ge 0} \beta^t M_t S_t'$, where $M_t$ is a shadow price and $S_t$ is a selection matrix. + +**Step 3: Solve for the Gorman weight.** + +The budget constraint pins down $\mu_j$: + +$$ +\mu_j = \frac{W_k + W_d - W_{c2}}{W_{c1} - W_g}. +$$ + +The numerator is household $j$'s wealth (initial capital plus present value of endowments minus the cost of the deviation consumption stream). + +The denominator is the net cost of consuming one unit of aggregate consumption (consumption value minus intermediate good value). + +**Step 4: Build selection matrices.** + +Finally, the code constructs selection matrices $S_{ci}, S_{hi}, S_{si}$ that map the augmented state $X_t = [h_{j,t-1}^\top, x_t^\top]^\top$ into household $j$'s allocations: + +$$ +c_{jt} = S_{ci} X_t, \quad h_{jt} = S_{hi} X_t, \quad s_{jt} = S_{si} X_t. +$$ + +The augmented state includes household $j$'s own lagged durable stock $h_{j,t-1}$ because the deviation term $\tilde{\chi}_{jt}$ depends on it through the inverse canonical representation. + +```{code-cell} ipython3 +def heter( + Λ, Θ_h, Δ_h, Π_h, β, + Θ_k, Δ_k, A_22, C_2, + U_bi, U_di, + A0, C, M_d, M_g, M_k, Γ, M_c, + x0, h0i, k0i, + S_h, S_k, S_i, S_g, S_d, S_b, S_c, S_s, + M_h, M_s, M_i, + tol=1e-15, +): + """ + Compute household i selection matrices, the Gorman weight μ_i, + and valuation objects. + """ + # Dimensions + n_s, n_h = np.asarray(Λ).shape + _, n_c = np.asarray(Θ_h).shape + n_z, n_w = np.asarray(C_2).shape + n_k, _ = np.asarray(Θ_k).shape + n_d, _ = np.asarray(U_di).shape + n_x = n_h + n_k + n_z + + β = float(np.asarray(β).squeeze()) + + # Household deviation problem (Chapter 3 scaling trick) + A = np.asarray(Δ_h, dtype=float) * np.sqrt(β) + B = np.asarray(Θ_h, dtype=float) * np.sqrt(β) + + Λ = np.asarray(Λ, dtype=float) + Π_h = np.asarray(Π_h, dtype=float) + U_bi = np.asarray(U_bi, dtype=float) + + R_hh = Λ.T @ Λ + tol * np.eye(n_h) + Q_hh = Π_h.T @ Π_h + S_hh = Π_h.T @ Λ + + if np.linalg.matrix_rank(Q_hh) < max(Q_hh.shape): + raise ValueError("The Π_h'Π_h block is singular.") + + Q_inv = np.linalg.inv(Q_hh) + + # Eliminate cross term + A = A - B @ Q_inv @ S_hh + R_hh = R_hh - S_hh.T @ Q_inv @ S_hh + + V11 = solve_discrete_are(A, B, R_hh, Q_hh) + Λ_s = np.linalg.solve(Q_hh + B.T @ V11 @ B, B.T @ V11 @ A) + + A0_dev = A - B @ Λ_s + Λ_s = Λ_s + Q_inv @ S_hh + + # Feedforward for U_bi z_t + A12 = -B @ Q_inv @ Π_h.T @ U_bi + R12 = Λ.T @ U_bi - Λ.T @ Π_h @ Q_inv @ Π_h.T @ U_bi + A0_dev.T @ V11 @ A12 + + V12 = doublej2(A0_dev.T, R12, A_22, np.eye(n_z)) + Q_opt = np.linalg.inv(Q_hh + B.T @ V11 @ B) + U_b_s = Q_opt @ B.T @ (V12 @ A_22 + V11 @ A12) + Q_inv @ Π_h.T @ U_bi + + # Undo √β scaling + A0_dev = A0_dev / np.sqrt(β) + + # Long-run covariance under aggregate law of motion + V_mat = doublej2(β * A0, C, A0, C) * β / (1 - β) + + # Present value of household endowment stream + U_di = np.asarray(U_di, dtype=float) + S_di = np.hstack((np.zeros((n_d, n_h + n_k)), U_di)) + W_d = doublej2(β * A0.T, M_d.T, A0.T, S_di.T) + W_d = x0.T @ W_d @ x0 + np.trace(M_d @ V_mat @ S_di.T) + + # Present value of intermediate good + W_g = doublej2(β * A0.T, M_g.T, A0.T, S_g.T) + W_g = x0.T @ W_g @ x0 + np.trace(M_g @ V_mat @ S_g.T) + + # Shadow price object for durables + M_hs = β * doublej2(β * A0_dev.T, Λ_s.T, A0.T, M_c.T) @ A0 + S_c1 = Θ_h.T @ M_hs - M_c + + # Augmented system for consumption valuation + A_s = np.block([[A0_dev, Θ_h @ S_c1], [np.zeros((n_x, n_h)), A0]]) + C_s = np.vstack((np.zeros((n_h, n_w)), C)) + + M_cs = np.hstack((np.zeros((M_c.shape[0], n_h)), M_c)) + S_cs = np.hstack((-Λ_s, S_c1)) + + x0s = np.vstack((np.zeros((n_h, 1)), x0)) + + W_c1 = doublej2(β * A_s.T, M_cs.T, A_s.T, S_cs.T) + V_s = doublej2(β * A_s, C_s, A_s, C_s) * β / (1 - β) + W_c1 = float((x0s.T @ W_c1 @ x0s + np.trace(M_cs @ V_s @ S_cs.T)).squeeze()) + + S_c2 = np.hstack((np.zeros((M_c.shape[0], n_h + n_k)), U_b_s)) + A_s2 = np.block([[A0_dev, Θ_h @ S_c2], [np.zeros((n_x, n_h)), A0]]) + S_cs2 = np.hstack((-Λ_s, S_c2)) + x0s2 = np.vstack((h0i, x0)) + + W_c2 = doublej2(β * A_s2.T, M_cs.T, A_s2.T, S_cs2.T) + V_s2 = doublej2(β * A_s2, C_s, A_s2, C_s) * β / (1 - β) + W_c2 = float((x0s2.T @ W_c2 @ x0s2 + np.trace(M_cs @ V_s2 @ S_cs2.T)).squeeze()) + + # Present value of initial capital + W_k = float((k0i.T @ ( + np.asarray(Δ_k).T @ M_k + + np.asarray(Γ).T @ M_d) @ x0).squeeze()) + + μ = float(((W_k + W_d - W_c2) / (W_c1 - W_g)).squeeze()) + + # Household selection matrices on augmented state X_t = [h^i_{t-1}, x_t] + S_cs = μ * S_c1 + S_c2 + A0_i = np.block([[A0_dev, Θ_h @ S_cs], [np.zeros((n_x, n_h)), A0]]) + S_ci = np.hstack((-Λ_s, S_cs)) + S_hi = A0_i[:n_h, :n_h + n_x] + S_si = np.hstack((Λ, np.zeros((n_s, n_x)))) + Π_h @ S_ci + S_bi = np.hstack((np.zeros((n_s, 2 * n_h + n_k)), U_bi)) + S_di = np.hstack((np.zeros((n_d, 2 * n_h + n_k)), U_di)) + + # Embed aggregate selection and pricing objects on X_t + S_ha = np.hstack((np.zeros((n_h, n_h)), S_h)) + S_ka = np.hstack((np.zeros((n_k, n_h)), S_k)) + S_ia = np.hstack((np.zeros((S_i.shape[0], n_h)), S_i)) + S_ga = np.hstack((np.zeros((S_g.shape[0], n_h)), S_g)) + S_da = np.hstack((np.zeros((n_d, n_h)), S_d)) + S_ba = np.hstack((np.zeros((n_s, n_h)), S_b)) + S_ca = np.hstack((np.zeros((S_c.shape[0], n_h)), S_c)) + S_sa = np.hstack((np.zeros((n_s, n_h)), S_s)) + + M_ha = np.hstack((np.zeros((M_h.shape[0], n_h)), M_h)) + M_ka = np.hstack((np.zeros((M_k.shape[0], n_h)), M_k)) + M_sa = np.hstack((np.zeros((M_s.shape[0], n_h)), M_s)) + M_ga = np.hstack((np.zeros((M_g.shape[0], n_h)), M_g)) + M_ia = np.hstack((np.zeros((M_i.shape[0], n_h)), M_i)) + M_da = np.hstack((np.zeros((M_d.shape[0], n_h)), M_d)) + M_ca = M_cs + + return { + "μ": μ, + "S_ci": S_ci, + "S_hi": S_hi, + "S_si": S_si, + "S_bi": S_bi, + "S_di": S_di, + "S_ha": S_ha, + "S_ka": S_ka, + "S_ia": S_ia, + "S_ga": S_ga, + "S_da": S_da, + "S_ba": S_ba, + "S_ca": S_ca, + "S_sa": S_sa, + "M_ha": M_ha, + "M_ka": M_ka, + "M_sa": M_sa, + "M_ga": M_ga, + "M_ia": M_ia, + "M_da": M_da, + "M_ca": M_ca, + "A0_i": A0_i, + "C_s": C_s, + "x0s": np.vstack((h0i, x0)), + } +``` + +The book sets the intermediate-good multiplier to $M_g = S_g$, so we pass `econ.Sg` for `M_g`. + +## Risk sharing implications + +The Gorman sharing rule has strong implications for risk sharing across households. + +Because the weight $\mu_j$ is time-invariant and determined at date zero, the deviation process $\{c_{jt} - \tilde{\chi}_{jt}\}$ exhibits perfect risk pooling: all households share aggregate consumption risk in fixed proportions. + +Non-separabilities in preferences (over time or across goods) affect only the baseline process $\{\tilde{\chi}_{jt}\}$ and the calculation of the risk-sharing coefficient $\mu_j$. They do not break the proportional sharing of aggregate risk. + +In the special case where the preference shock processes $\{b_{jt}\}$ are deterministic (known at time zero), individual consumption is perfectly correlated with aggregate consumption conditional on initial information. + +The figures we will be showing below confirm this: household consumption paths track aggregate consumption closely. + +## Limited markets + +This section implements a key result: the Arrow-Debreu allocation can be replicated using only a mutual fund and a one-period bond, rather than a complete set of contingent claims. + +### The trading mechanism + +Consider an economy where each household $j$ initially owns claims to its own endowment stream $\{d_{jt}\}$. Instead of trading in a complete set of Arrow-Debreu markets, we open: + +1. A stock market with securities paying dividends $\{d_{jt}\}$ for each endowment process +2. A market for one-period riskless bonds + +At time zero, household $j$ executes the following trades: + +1. **Sell individual claims**: Household $j$ sells all shares of its own endowment security +2. **Buy the mutual fund**: Purchase $\mu_j$ shares of all securities (equivalently, $\mu_j$ shares of a mutual fund holding the aggregate endowment) +3. **Adjust bond position**: Take position $\hat{k}_{j0}$ in the one-period bond + +After this initial rebalancing, household $j$ holds the portfolio forever. + +### Why $\mu_j$ shares? + +The portfolio weight $\mu_j$ is not arbitrary — it is the unique weight that allows the limited-markets portfolio to replicate the Arrow-Debreu consumption allocation. + +**The requirement.** Under Gorman aggregation, household $j$'s equilibrium consumption satisfies + +$$ +c_{jt} = \mu_j c_t + \tilde{\chi}_{jt}. +$$ + +We need a portfolio strategy that delivers exactly this consumption stream. + +**The mutual fund.** The mutual fund holds claims to all individual endowment streams. Total dividends paid by the fund each period are + +$$ +\sum_{i=1}^J d_{it} = d_t, +$$ + +the aggregate endowment. If household $j$ holds fraction $\theta_j$ of this fund, it receives $\theta_j d_t$ in dividends. + +**Matching the proportional term.** The proportional part of consumption $\mu_j c_t$ must be financed by the mutual fund and capital holdings. Since the aggregate resource constraint is + +$$ +c_t + i_t = (\delta_k + \gamma_1) k_{t-1} + d_t, +$$ + +holding $\theta_j$ shares of aggregate output (capital income plus endowments) delivers $\theta_j [(\delta_k + \gamma_1)k_{t-1} + d_t]$. For this to finance $\mu_j c_t$ plus reinvestment $\mu_j k_t$, we need $\theta_j = \mu_j$. + +**The deviation term.** The remaining term $\tilde{\chi}_{jt}$ is financed by adjusting the bond position according to the recursion derived below. + +**Conclusion.** Setting $\theta_j = \mu_j$ ensures that the mutual fund finances exactly the proportional share $\mu_j c_t$, while the bond handles the deviation $\tilde{\chi}_{jt}$. This transforms heterogeneous endowment risk into proportional shares of aggregate risk. + +### Derivation of the bond recursion + +We now derive the law of motion for the bond position $\hat{k}_{jt}$. + +**Step 1: Write the budget constraint.** + +Household $j$'s time-$t$ resources equal uses: + +$$ +\underbrace{\mu_j [(\delta_k + \gamma_1) k_{t-1} + d_t]}_{\text{mutual fund income}} + \underbrace{R \hat{k}_{j,t-1}}_{\text{bond return}} = \underbrace{c_{jt}}_{\text{consumption}} + \underbrace{\mu_j k_t}_{\text{new fund shares}} + \underbrace{\hat{k}_{jt}}_{\text{new bonds}} +$$ + +where $R := \delta_k + \gamma_1$ is the gross return. + +**Step 2: Substitute the sharing rule.** + +Replace $c_{jt}$ with $\mu_j c_t + \tilde{\chi}_{jt}$: + +$$ +\mu_j [(\delta_k + \gamma_1) k_{t-1} + d_t] + R \hat{k}_{j,t-1} = \mu_j c_t + \tilde{\chi}_{jt} + \mu_j k_t + \hat{k}_{jt} +$$ + +**Step 3: Use the aggregate resource constraint.** + +The aggregate economy satisfies $c_t + k_t = (\delta_k + \gamma_1) k_{t-1} + d_t$, so: + +$$ +(\delta_k + \gamma_1) k_{t-1} + d_t = c_t + k_t +$$ + +Substituting into the left-hand side: + +$$ +\mu_j (c_t + k_t) + R \hat{k}_{j,t-1} = \mu_j c_t + \tilde{\chi}_{jt} + \mu_j k_t + \hat{k}_{jt} +$$ + +**Step 4: Simplify.** + +The $\mu_j c_t$ and $\mu_j k_t$ terms cancel: + +$$ +R \hat{k}_{j,t-1} = \tilde{\chi}_{jt} + \hat{k}_{jt} +$$ + +Rearranging gives the bond recursion: + +$$ +\hat{k}_{jt} = R \hat{k}_{j,t-1} - \tilde{\chi}_{jt}. +$$ (eq:bond-recursion) + +This says that the bond position grows at rate $R$ but is drawn down by the deviation consumption $\tilde{\chi}_{jt}$. When $\tilde{\chi}_{jt} > 0$ (household $j$ consumes more than its share), it finances this by running down its bond holdings. + +### Initial bond position + +To find $\hat{k}_{j0}$, we solve the recursion {eq}`eq:bond-recursion` forward. + +**Step 1: Iterate forward.** + +From $\hat{k}_{jt} = R \hat{k}_{j,t-1} - \tilde{\chi}_{jt}$, we get: + +$$ +\begin{aligned} +\hat{k}_{j1} &= R \hat{k}_{j0} - \tilde{\chi}_{j1} \\ +\hat{k}_{j2} &= R \hat{k}_{j1} - \tilde{\chi}_{j2} = R^2 \hat{k}_{j0} - R\tilde{\chi}_{j1} - \tilde{\chi}_{j2} \\ +&\vdots \\ +\hat{k}_{jT} &= R^T \hat{k}_{j0} - \sum_{t=1}^T R^{T-t} \tilde{\chi}_{jt} +\end{aligned} +$$ + +**Step 2: Apply transversality.** + +For the budget constraint to hold with equality, we need $\lim_{T \to \infty} R^{-T} \hat{k}_{jT} = 0$. Dividing by $R^T$: + +$$ +R^{-T} \hat{k}_{jT} = \hat{k}_{j0} - \sum_{t=1}^T R^{-t} \tilde{\chi}_{jt} +$$ + +Taking $T \to \infty$ and using transversality: + +$$ +0 = \hat{k}_{j0} - \sum_{t=1}^\infty R^{-t} \tilde{\chi}_{jt} +$$ + +**Step 3: Solve for initial position.** + +$$ +\hat{k}_{j0} = \sum_{t=1}^\infty R^{-t} \tilde{\chi}_{jt}. +$$ + +This is the present value of future deviation consumption. Since $\tilde{\chi}_{jt}$ depends only on the deterministic deviation between household $j$'s preference shocks and its share of aggregate preference shocks, this sum is in the time-zero information set and can be computed at date zero. + +The household's total asset position is $a_{jt} = \mu_j k_t + \hat{k}_{jt}$. + +```{code-cell} ipython3 +def compute_household_paths(econ, U_b_list, U_d_list, x0, x_path, γ_1, Λ, h0i=None, k0i=None): + """ + Compute household allocations and limited-markets portfolios + along a fixed aggregate path. + """ + x0 = np.asarray(x0, dtype=float).reshape(-1, 1) + x_path = np.asarray(x_path, dtype=float) + + Θ_h = np.atleast_2d(econ.thetah) + Δ_h = np.atleast_2d(econ.deltah) + Π_h = np.atleast_2d(econ.pih) + Λ = np.atleast_2d(Λ) + + n_h = Θ_h.shape[0] + n_k = np.atleast_2d(econ.thetak).shape[0] + + z_path = x_path[n_h + n_k:, :] + + c = econ.Sc @ x_path + k = econ.Sk @ x_path + b = econ.Sb @ x_path + d = econ.Sd @ x_path + + δ_k = float(np.asarray(econ.deltak).squeeze()) + R = δ_k + float(γ_1) + + Π_inv = np.linalg.inv(Π_h) + A_h = Δ_h - Θ_h @ Π_inv @ Λ + B_h = Θ_h @ Π_inv + + if h0i is None: + h0i = np.zeros((n_h, 1)) + if k0i is None: + k0i = np.zeros((n_k, 1)) + + N = len(U_b_list) + _, T = c.shape + + μ = np.empty(N) + χ_tilde = np.zeros((N, T)) + c_j = np.zeros((N, T)) + d_j = np.zeros((N, T)) + d_share = np.zeros((N, T)) # Dividend income under limited markets + k_share = np.zeros((N, T)) + k_hat = np.zeros((N, T)) + a_total = np.zeros((N, T)) + + for j in range(N): + U_bj = np.asarray(U_b_list[j], dtype=float) + U_dj = np.asarray(U_d_list[j], dtype=float) + + res = heter( + econ.llambda, + econ.thetah, + econ.deltah, + econ.pih, + econ.beta, + econ.thetak, + econ.deltak, + econ.a22, + econ.c2, + U_bj, + U_dj, + econ.A0, + econ.C, + econ.Md, + econ.Sg, + econ.Mk, + econ.gamma, + econ.Mc, + x0, + h0i, + k0i, + econ.Sh, + econ.Sk, + econ.Si, + econ.Sg, + econ.Sd, + econ.Sb, + econ.Sc, + econ.Ss, + econ.Mh, + econ.Ms, + econ.Mi, + ) + μ[j] = res["μ"] + + b_tilde = U_bj @ z_path - μ[j] * b + + η = np.zeros((n_h, T + 1)) + η[:, 0] = np.asarray(h0i).reshape(-1) + for t in range(1, T): + χ_tilde[j, t] = (-Π_inv @ Λ @ η[:, t - 1] + Π_inv @ b_tilde[:, t]).squeeze() + η[:, t] = (A_h @ η[:, t - 1] + B_h @ b_tilde[:, t]).squeeze() + + c_j[j] = (μ[j] * c[0] + χ_tilde[j]).squeeze() + # Original endowment claim (before trading) + d_j[j] = (U_dj @ z_path)[0, :] + # Dividend income under limited markets: μ_j × aggregate endowment + d_share[j] = (μ[j] * d[0]).squeeze() + # Capital share in mutual fund + k_share[j] = (μ[j] * k[0]).squeeze() + + if abs(R - 1.0) >= 1e-14: + k_hat[j, -1] = χ_tilde[j, -1] / (R - 1.0) + for t in range(T - 1, 0, -1): + k_hat[j, t - 1] = (k_hat[j, t] + χ_tilde[j, t]) / R + + a_total[j] = k_share[j] + k_hat[j] + + return { + "μ": μ, + "χ_tilde": χ_tilde, + "c": c, + "k": k, + "d": d, + "c_j": c_j, + "d_j": d_j, + "d_share": d_share, # μ_j × d_t: dividend income under limited markets + "k_share": k_share, + "k_hat": k_hat, + "a_total": a_total, + "x_path": x_path, + "z_path": z_path, + "R": R, + } +``` + +```{code-cell} ipython3 +def simulate_state_path(A0, C, x0, ts_length, rng=None): + """ + Simulate x_{t+1} = A0 x_t + C w_{t+1} with iid standard normal shocks. + """ + rng = np.random.default_rng(rng) + x0 = np.asarray(x0, dtype=float).reshape(-1, 1) + n_x = x0.shape[0] + n_w = np.asarray(C).shape[1] + + x = np.empty((n_x, ts_length)) + w = np.empty((n_w, ts_length)) + x[:, 0] = x0[:, 0] + w[:, 0] = 0.0 + + for t in range(ts_length - 1): + w[:, t + 1] = rng.standard_normal(n_w) + x[:, t + 1] = (A0 @ x[:, t] + C @ w[:, t + 1]).reshape(-1) + + return x, w +``` + +(gorman_twohh)= +## Example: two-household economy + +We reproduce the two-household Hall-style calibration from Chapter 12.6 of {cite:t}`HS2013`. + +```{code-cell} ipython3 +ϕ_1 = 1e-5 +γ_1 = 0.1 +δ_k = 0.95 +β = 1.0 / (γ_1 + δ_k) + +θ_k = 1.0 +δ_h = 0.2 +θ_h = 0.1 +Λ = 0.0 +Π_h = 1.0 + +Φ_c = np.array([[1.0], [0.0]]) +Φ_g = np.array([[0.0], [1.0]]) +Φ_i = np.array([[1.0], [-ϕ_1]]) +Γ = np.array([[γ_1], [0.0]]) + +A_22 = np.array([ + [1.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 1.2, -0.22, 0.0, 0.0], + [0.0, 1.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0], +], dtype=float) + +C_2 = np.array([ + [0.0, 0.0], + [0.0, 0.25], + [0.0, 0.0], + [1.0, 0.0], + [0.0, 1.0], +], dtype=float) + +U_b1 = np.array([[15.0, 0.0, 0.0, 0.0, 0.0]]) +U_b2 = np.array([[15.0, 0.0, 0.0, 0.0, 0.0]]) +U_b = U_b1 + U_b2 + +U_d1 = np.array([[4.0, 0.0, 0.0, 0.2, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0]]) +U_d2 = np.array([[3.0, 1.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0]]) +U_d = U_d1 + U_d2 + +info = (A_22, C_2, U_b, U_d) +tech = (Φ_c, Φ_g, Φ_i, Γ, np.array([[δ_k]]), np.array([[θ_k]])) +pref = (np.array([[β]]), + np.array([[Λ]]), + np.array([[Π_h]]), + np.array([[δ_h]]), + np.array([[θ_h]])) + +econ = DLE(info, tech, pref) +``` + +We simulate the closed-loop DLE state and compute household paths. + +```{code-cell} ipython3 +x0 = np.array([[0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0]]).T +ts_length = 2_000 + +x_path, _ = simulate_state_path(econ.A0, econ.C, x0, ts_length, rng=1234) + +paths = compute_household_paths( + econ=econ, + U_b_list=[U_b1, U_b2], + U_d_list=[U_d1, U_d2], + x0=x0, + x_path=x_path, + γ_1=γ_1, + Λ=Λ, +) + +paths["μ"] +``` + +```{code-cell} ipython3 +--- +mystnb: + figure: + caption: consumption paths + name: fig-gorman-consumption +--- +T_plot = 250 +fig, ax = plt.subplots() +ax.plot(paths["c"][0, :T_plot], lw=2, label="aggregate") +ax.plot(paths["c_j"][0, :T_plot], lw=2, label="household 1") +ax.plot(paths["c_j"][1, :T_plot], lw=2, label="household 2") +ax.set_xlabel("time") +ax.set_ylabel("consumption") +ax.legend() +plt.show() +``` + +The next figure plots the limited-markets bond adjustment and confirms that the adjustments sum to approximately zero. + +```{code-cell} ipython3 +--- +mystnb: + figure: + caption: bond adjustment positions + name: fig-gorman-bond-adjustment +--- +fig, ax = plt.subplots() +ax.plot(paths["k_hat"][0, :T_plot], lw=2, label="household 1") +ax.plot(paths["k_hat"][1, :T_plot], lw=2, label="household 2") +ax.plot(paths["k_hat"][:, :T_plot].sum(axis=0), lw=2, label="sum") +ax.axhline(0.0, color="k", lw=1, alpha=0.5) +ax.set_xlabel("time") +ax.set_ylabel("bond position") +ax.legend() +plt.show() +``` + +```{code-cell} ipython3 +np.max(np.abs(paths["k_hat"].sum(axis=0))) +``` + + +## Many-Household Gorman Economies + +This section presents a scalable Gorman economy specification that accommodates many households with heterogeneous endowments and preferences. + +### Specification + +The exogenous state vector is + +$$ +z_t = \begin{bmatrix} 1 \\ d_{a,t} \\ d_{a,t-1} \\ w_{2,t} \\ \vdots \\ w_{n,t} \\ w_{n+1,t} \\ \vdots \\ w_{2n,t} \end{bmatrix}, +$$ + +where the first three components track a constant and the "ghost" aggregate endowment process, and the remaining components are i.i.d. shocks for individual endowments ($w_{2,t}, \ldots, w_{n,t}$) and preference shocks ($w_{n+1,t}, \ldots, w_{2n,t}$). + +The aggregate endowment follows an AR(2) process: + +$$ +d_{a,t} = \rho_1 d_{a,t-1} + \rho_2 d_{a,t-2} + \sigma_a w_{1,t}, +$$ + +where we can set the $\rho_j$'s to capture persistent aggregate fluctuations. + +Individual endowments are: + +$$ +d_{jt} = \alpha_j + \phi_j d_{a,t} + \sigma_j w_{j,t}, \quad j = 2, \ldots, n, +$$ + +and for household 1 (which absorbs the negative of all idiosyncratic shocks to ensure aggregation): + +$$ +d_{1t} = \alpha_1 + \phi_1 d_{a,t} - \sum_{j=2}^n \sigma_j w_{j,t}. +$$ + +This construction ensures that + +$$ +\sum_{j=1}^n d_{j,t} = \sum_{j=1}^n \alpha_j + \left(\sum_{j=1}^n \phi_j\right) d_{a,t}. +$$ + +Imposing $\sum_{j=1}^n \phi_j = 1$ gives + +$$ +\sum_{j=1}^n d_{j,t} = \sum_{j=1}^n \alpha_j + d_{a,t}. +$$ + +Preference shocks are muted to simplify initial experiments: + +$$ +b_{jt} = \bar{b} + \gamma_j w_{n+j,t}, \quad j = 1, \ldots, n, +$$ + +where the $\gamma_j$'s are small. + +```{code-cell} ipython3 +def build_reverse_engineered_gorman_extended( + n, + rho1, rho2, sigma_a, + alphas, phis, sigmas, + b_bar, gammas, + rho_idio=0.0, + rho_pref=0.0, + n_absorb=None, +): + """ + Extended version that includes idiosyncratic shocks as state variables, + allowing the full heterogeneous dynamics to be captured. + + The state vector is: + z_t = [1, d_{a,t}, d_{a,t-1}, eta_{n_absorb+1,t}, ..., eta_{n,t}, xi_{1,t}, ..., xi_{n,t}] + + The first n_absorb households absorb the negative sum of all idiosyncratic shocks + to ensure shocks sum to zero (Gorman requirement): + sum_{j=1}^{n_absorb} (-1/n_absorb * sum_{k>n_absorb} eta_k) + sum_{k>n_absorb} eta_k = 0 + + Each household k > n_absorb has its own idiosyncratic endowment shock eta_{k,t} + following an AR(1): + + eta_{k,t+1} = rho_idio[k] * eta_{k,t} + sigma_k * w_{k,t+1} + + with rho_idio = 0 recovering i.i.d. shocks (AR(0)). + + Preference shocks xi_{j,t} are included for each household. + + Parameters + ---------- + n_absorb : int, optional + Number of households that absorb the idiosyncratic shocks. + If None, defaults to max(1, n // 10) (10% of households, at least 1). + """ + alphas = np.asarray(alphas).reshape(-1) + phis = np.asarray(phis).reshape(-1) + sigmas = np.asarray(sigmas).reshape(-1) + gammas = np.asarray(gammas).reshape(-1) + + assert len(alphas) == len(phis) == len(sigmas) == len(gammas) == n + + # Default: 10% of households absorb shocks (at least 1) + if n_absorb is None: + n_absorb = max(1, n // 10) + n_absorb = int(n_absorb) + if n_absorb < 1 or n_absorb >= n: + raise ValueError(f"n_absorb must be in [1, n-1], got {n_absorb} with n={n}") + + # State vector: z_t = [1, d_{a,t}, d_{a,t-1}, eta_{n_absorb+1,t}, ..., eta_{n,t}, xi_{1,t}, ..., xi_{n,t}] + # where eta_{j,t} are idiosyncratic endowment shocks (j=n_absorb+1..n) + # and xi_{j,t} are preference shocks (j=1..n) + # First n_absorb households absorb -1/n_absorb * sum(eta_j) to ensure shocks sum to zero + # Dimension: 3 + (n - n_absorb) + n = 2n + 3 - n_absorb + + n_idio = n - n_absorb # eta_{n_absorb+1}, ..., eta_n + n_pref = n # xi_1, ..., xi_n + nz = 3 + n_idio + n_pref + nw = 1 + n_idio + n_pref # aggregate + idio + pref shocks + + # Persistence parameters (scalars broadcast to vectors) + rho_idio = np.asarray(rho_idio, dtype=float) + if rho_idio.ndim == 0: + rho_idio = np.full(n_idio, float(rho_idio)) + if rho_idio.shape != (n_idio,): + raise ValueError(f"rho_idio must be scalar or shape ({n_idio},), got {rho_idio.shape}") + + rho_pref = np.asarray(rho_pref, dtype=float) + if rho_pref.ndim == 0: + rho_pref = np.full(n_pref, float(rho_pref)) + if rho_pref.shape != (n_pref,): + raise ValueError(f"rho_pref must be scalar or shape ({n_pref},), got {rho_pref.shape}") + + # A22: transition matrix + A22 = np.zeros((nz, nz)) + A22[0, 0] = 1.0 # constant + A22[1, 1] = rho1 # d_{a,t} AR(2) first coef + A22[1, 2] = rho2 # d_{a,t} AR(2) second coef + A22[2, 1] = 1.0 # lag transition + for j in range(n_idio): + A22[3 + j, 3 + j] = rho_idio[j] + for j in range(n_pref): + A22[3 + n_idio + j, 3 + n_idio + j] = rho_pref[j] + + # C2: shock loading + C2 = np.zeros((nz, nw)) + C2[1, 0] = sigma_a # aggregate shock -> d_{a,t} + for j in range(n_idio): + # Map to households n_absorb+1, ..., n (indices n_absorb, ..., n-1 in 0-based) + C2[3 + j, 1 + j] = sigmas[n_absorb + j] + for j in range(n_pref): + C2[3 + n_idio + j, 1 + n_idio + j] = gammas[j] # gamma_j -> xi_j + + # Ud_per_house: endowment loading + # First n_absorb households: d_{jt} = alpha_j + phi_j * d_{a,t} - (1/n_absorb) * sum_{k>n_absorb} eta_{k,t} + # Remaining households k > n_absorb: d_{kt} = alpha_k + phi_k * d_{a,t} + eta_{k,t} + Ud_per_house = [] + for j in range(n): + block = np.zeros((2, nz)) + block[0, 0] = alphas[j] # constant + block[0, 1] = phis[j] # loading on d_{a,t} + + if j < n_absorb: + # Absorbing households: load -1/n_absorb on each idiosyncratic shock + for k in range(n_idio): + block[0, 3 + k] = -1.0 / n_absorb + else: + # Non-absorbing household j loads on its own shock eta_j + # Household j (j >= n_absorb) corresponds to eta_{j+1} at position 3 + (j - n_absorb) + block[0, 3 + (j - n_absorb)] = 1.0 + + Ud_per_house.append(block) + + Ud = sum(Ud_per_house) + + # Ub_per_house: bliss loading + # b_{jt} = b_bar + xi_{j,t} + Ub_per_house = [] + for j in range(n): + row = np.zeros((1, nz)) + row[0, 0] = b_bar # constant bliss + row[0, 3 + n_idio + j] = 1.0 # loading on xi_j + Ub_per_house.append(row) + + Ub = sum(Ub_per_house) + + # Initial state + x0 = np.zeros((nz, 1)) + x0[0, 0] = 1.0 # constant = 1 + + return A22, C2, Ub, Ud, Ub_per_house, Ud_per_house, x0 +``` + +### Many-Household Example + +We now instantiate a 100-household economy using the reverse-engineered Gorman specification. + +```{code-cell} ipython3 +np.random.seed(42) +N = 100 + +# AR(2) parameters for aggregate endowment +# rho1 = 1.3435 +# rho2 = -0.9025 +rho1 = 0.95 +rho2 = 0.0 + +sigma_a = 0.5 + +# Individual endowment parameters +# alphas: intercepts (drawn randomly for each household) +alphas = np.random.uniform(3.0, 5.0, N) + +# phis: loadings on aggregate endowment (must sum to 1) +phis_raw = np.random.uniform(0.5, 1.5, N) +phis = phis_raw / np.sum(phis_raw) # normalize to sum to 1 + +wealth_rank_proxy = np.argsort(np.argsort(alphas)) # 0 = lowest alpha, N-1 = highest +wealth_pct_proxy = (wealth_rank_proxy + 0.5) / N # in (0, 1) +poorness = 1.0 - wealth_pct_proxy # 1 = poorest, 0 = richest + +# Number of households that absorb idiosyncratic shocks (Gorman requirement) +# Must be defined BEFORE creating rho_idio array +n_absorb = 50 + +# sigmas: idiosyncratic endowment shock scales +# Each household has a sigma, but only households n_absorb+1..N have their own eta state +sigma_idio_min, sigma_idio_max = 0.2, 5.0 +sigmas = sigma_idio_min + (sigma_idio_max - sigma_idio_min) * (poorness ** 2.0) + +# rho_idio: persistence for eta_{n_absorb+1..N} +# Only households n_absorb+1 through N have independent idiosyncratic shocks +rho_idio_min, rho_idio_max = 0.0, 0.98 +rho_idio = rho_idio_min + (rho_idio_max - rho_idio_min) * (poorness[n_absorb:] ** 1.0) + +# Preference parameters +b_bar = 5.0 +# Idiosyncratic preference shocks (turn on to generate household-specific consumption movements) +enable_pref_shocks = False # ENABLE to see time-varying consumption heterogeneity +pref_shock_scale = 0.5 # Increase scale to see clearer effects +pref_shock_persistence = 0.7 + +if enable_pref_shocks: + gammas_pref = pref_shock_scale * np.ones(N) + rho_pref = pref_shock_persistence +else: + gammas_pref = np.zeros(N) # mute preference shocks; shock endowment only + rho_pref = 0.0 # keep preference states deterministic at 0 + +# Burn in for the aggregate and individual series +burn_in = 200 + +# Build the economy +A22, C2, Ub, Ud, Ub_list, Ud_list, x0 = build_reverse_engineered_gorman_extended( + n=N, + rho1=rho1, rho2=rho2, sigma_a=sigma_a, + alphas=alphas, phis=phis, sigmas=sigmas, + b_bar=b_bar, gammas=gammas_pref, + rho_idio=rho_idio, rho_pref=rho_pref, + n_absorb=n_absorb, +) + +print(f"State dimension nz = {A22.shape[0]}") +print(f"Shock dimension nw = {C2.shape[1]}") +print(f"sum(phis) = {np.sum(phis):.6f} (should be 1.0)") +``` + +```{code-cell} ipython3 +# Initialize economy via DLE for the reverse-engineered Gorman case. +info_ar1 = (A22, C2, Ub, Ud) +pref_ar1 = (np.array([[beta]]), + np.array([[lam]]), + np.array([[pih]]), + np.array([[deltah]]), + np.array([[thetah]])) +tech_ar1 = (phic, phig, phii, gamma, + np.array([[deltak]]), + np.array([[thetak]])) + +paths, econ = solve_model(info_ar1, tech_ar1, pref_ar1, + Ub_list, Ud_list, gamma1, lam, z0=x0) +``` + +The following figure shows household-level consumption, endowments, and bond positions. + +```{code-cell} ipython3 +T = 50 + +plt.figure(figsize=(14, 8)) + +# Individual consumption paths +plt.subplot(2, 2, 1) +plt.title('Consumption (N households)') +plt.plot(paths["c_j"][:, 1:T].T) + +# Individual dividends +plt.subplot(2, 2, 2) +plt.title('Dividends (N households)') +plt.plot(paths["d_j"][:, 1:T].T) + +# Bond purchases k̂_jt +plt.subplot(2, 2, 3) +plt.title(r'Bond purchases $\hat{k}_{jt}$') +plt.plot(paths["khat_j"][:, :T].T) + +# Total bonds +plt.subplot(2, 2, 4) +plt.title('Total Bonds $b_{jt}$') +plt.plot(paths["bonds_j"][:, :T].T) + +plt.tight_layout() +plt.show() +``` + +## State-Space Representation + +This section connects the Gorman economy to its underlying linear state-space representation. + +### Closed-Loop Dynamics + +The aggregate planning problem has a linear-quadratic structure. Following Chapter 5 of {cite:t}`HS2013`, the state and control are + +$$ +x_t = +\begin{bmatrix} +h_{t-1} \\ +k_{t-1} \\ +z_t +\end{bmatrix}, +\qquad +u_t := i_t, +\qquad +z_{t+1} = A_{22} z_t + C_2 w_{t+1}, +$$ + +where $h_{t-1}$ is the household service stock, $k_{t-1}$ is capital, and $z_t$ stacks exogenous variables. + +The controlled transition is + +$$ +x_{t+1} = A x_t + B u_t + C w_{t+1}. +$$ + +Solving the LQ problem yields optimal feedback $u_t = -F x_t$, giving the closed-loop law of motion + +$$ +x_{t+1} = A_0 x_t + C w_{t+1}, \qquad A_0 := A - BF. +$$ + +### Selection Matrices + +All equilibrium quantities are linear functions of the state. The `quantecon.DLE` class provides selection matrices $S_\bullet$ such that + +$$ +c_t = S_c x_t, \quad i_t = S_i x_t, \quad k_t = S_k x_t, \quad d_t = S_d x_t, \quad \text{etc.} +$$ + +Stacking these gives a measurement equation $y_t = G x_t$ where $G$ collects the relevant selection matrices. + +```{code-cell} ipython3 +# Closed-loop state space matrices +A0 = econ.A0 +C = econ.C + +print(f"State dimension: {A0.shape[0]}") +print(f"Shock dimension: {C.shape[1]}") + +# Dominant eigenvalues of the closed-loop dynamics +eigvals = np.linalg.eigvals(A0) +print(f"Largest eigenvalues: {np.sort(np.abs(eigvals))[-3:][::-1]}") +``` + +The following figure shows selected components of the aggregate state $x_t$. + +```{code-cell} ipython3 +xp = paths["xp"] +T_plot = 250 + +plt.figure(figsize=(10, 4)) +plt.plot(xp[1, :T_plot], label=r"$k_{t-1}$ (capital)") +plt.plot(xp[3, :T_plot], label=r"$d_{a,t}$ (aggregate endowment)") +plt.xlabel("t") +plt.legend() +plt.tight_layout() +plt.show() +``` + + +## Redistribution via Pareto Weight Reallocation + +This section develops a framework for analyzing the distributional effects of tax-and-transfer schemes within the Gorman aggregation model. The key insight is that we can reinterpret the competitive equilibrium consumption allocation in terms of Pareto weights, then consider alternative weight distributions that implement redistribution while preserving aggregate consumption and capital accumulation. + +### Competitive Equilibrium and Pareto Weights + +Start with the competitive equilibrium consumption allocation described by equation (12.4.4) on page 264 of Hansen and Sargent (2013): + +$$ +c_{jt} - \chi_{jt} = (u_j/u_a) (c_{at} - \chi_{at}), +$$ + +where: +- The $a$ subscript pertains to the representative agent +- $c_{at}$ and $\chi_{at}$ are computed from the representative agent problem +- The $j$ subscript pertains to the $j$th household +- $c_{jt}$ and $\chi_{jt}$ are computed via the Gorman aggregation framework (the `heter` function above) + +Recall from {eq}`eq:sharing_rule` that the Gorman sharing rule gives $c_{jt} = \mu_j c_t + \tilde{\chi}_{jt}$ where $\mu_j := u_j/u_a$ is household $j$'s wealth share. This $\mu_j$ directly corresponds to the implicit Pareto weight attached to individual $j$ in the competitive equilibrium. + +Define the Pareto weight vector: + +$$ +\lambda_j := \frac{u_j}{u_a} = \mu_j, +$$ + +where the Pareto weights are determined by the initial distribution of wealth (the assignment of $\{k_{j,-1}\}_j$ and the endowment processes $\{d_{jt}\}_j$ across households $j \in \{1, \ldots, J\}$). + +A fundamental property of these weights is that they sum to unity: + +$$ +\sum_{j=1}^J \lambda_j = \sum_{j=1}^J \frac{u_j}{u_a} = 1. +$$ + +This follows from the construction of the representative agent, whose utility weight $u_a$ is the sum of individual household utility weights (see equation {eq}`eq:agg_preference_aggregates`). + +Using the Pareto weight notation, we can rewrite equation (1) as: + +$$ +c_{jt} - \chi_{jt} = \lambda_j (c_{at} - \chi_{at}). +$$ + +This expression clarifies that household $j$'s consumption net of the deviation term is proportional to aggregate consumption net of its deviation, with the proportion determined by the Pareto weight $\lambda_j$. + +### Redistribution Proposal + +Consider an efficient tax-and-transfer scheme that leaves aggregate consumption and capital accumulation unaltered but redistributes incomes in a way consistent with a new set of Pareto weights $\{\lambda_j^*\}_{j=1}^J$ satisfying: + +$$ +\lambda_j^* \geq 0 \quad \forall j, \qquad \sum_{j=1}^J \lambda_j^* = 1. +$$ + +The associated competitive equilibrium consumption allocation under the new Pareto weights is: + +$$ +c_{jt} - \chi_{jt} = \lambda_j^* (c_{at} - \chi_{at}). +$$ + +Since the aggregate allocation $(c_{at}, k_{at}, \chi_{at})$ is unchanged, this redistribution preserves efficiency while reallocating consumption across households according to the new weights. + +### Post-Tax-and-Transfer Consumption and Income + +In what follows we treat the model’s household consumption panel $\{c_{jt}\}$ as our “CEX-like” consumption measure. + +For income, recall from Section 12.6 (equation {eq}`eq:lm_budget`) that in the limited-markets implementation, household $j$'s budget satisfies: + +$$ +c_{jt} - \chi_{jt} + \mu_j \chi_{at} + \mu_j k_t = \mu_j[(\delta_k + \gamma) k_{t-1} + d_t]. +$$ + +In this Hall-style calibration, a share-based “income” concept like $\lambda_j^*\big[(\delta_k + \gamma - 1)k_{t-1}+d_t\big]$ tends to move almost one-for-one with $\lambda_j^* c_t$ because aggregate investment is tiny and the resource constraint implies $c_t \approx (\delta_k + \gamma - 1)k_{t-1}+d_t$. + +To get an income panel with idiosyncratic risk (closer to what’s used empirically), we instead construct household private income from (i) household endowments and (ii) net asset income from the limited-markets asset position. Let $R := \delta_k + \gamma$ be the gross return and let $a_{j,t}$ denote household $j$’s total asset position (in code: `paths["bonds_j"]`). Define + +$$ +y^{pre}_{j,t} := d_{j,t} + (R-1)a_{j,t-1}. +$$ + +We then define disposable (post-tax-and-transfer) income via a balanced-budget tax-and-transfer rule that shrinks idiosyncratic variation by a factor $(1-\tau)$ while holding aggregate income fixed: + +$$ +y^{post}_{j,t} := (1-\tau) y^{pre}_{j,t} + \tau\,\bar y^{pre}_t, \qquad \bar y^{pre}_t := \frac{1}{J}\sum_{j=1}^J y^{pre}_{j,t}. +$$ + +Here $\tau \in [0,1]$ indexes redistribution/insurance intensity: $\tau=0$ is no redistribution, while $\tau=1$ fully pools income cross-sectionally each date. + +### Pre-Tax Private Income + +We compare consumption to $y^{pre}_{j,t}$ and $y^{post}_{j,t}$ using standard “consumption insurance” metrics (pass-through of idiosyncratic income shocks to idiosyncratic consumption) and percentile plots. + + +The difference between equations (6)-(7) and equations (8)-(9) captures the redistributive impact of the tax-and-transfer system, measured by the change from $\lambda_j$ to $\lambda_j^*$. + +To implement a more egalitarian distribution, we construct new Pareto weights $\{\lambda_j^*\}_{j=1}^J$ that: + +1. Lower the weights for low-$j$ types (wealthier households in our ordering) +2. Increase the weights for high-$j$ types (less wealthy households) +3. Leave middle-$j$ types relatively unaffected +4. Preserve the constraint $\sum_{j=1}^J \lambda_j^* = 1$ + +We implement this using a smooth transformation. Let $\{\lambda_j\}_{j=1}^J$ denote the original competitive equilibrium Pareto weights (sorted in descending order). Define the redistribution function: + +$$ +f(j; J) = \frac{j-1}{J-1}, \qquad +g(j; J) = 2\left|f(j; J) - \frac{1}{2}\right|, \qquad +\tau(j; J, \alpha, \beta) = \alpha \cdot \left[g(j; J)\right]^\beta, +$$ + +where: +- $j \in \{1, \ldots, J\}$ is the household index +- $\alpha > 0$ controls the overall magnitude of redistribution (with $\tau$ maximized at the extremes) +- $\beta > 1$ controls the progressivity (higher $\beta$ concentrates redistribution more strongly in the tails) + +The redistributed Pareto weights are: + +$$ +\tilde{\lambda}_j = \lambda_j + \tau(j; J, \alpha, \beta) \cdot (\bar{\lambda} - \lambda_j), +$$ + +where $\bar{\lambda} = J^{-1}$ is the equal-weight benchmark. + +To ensure the weights sum to unity, we normalize: + +$$ +\lambda_j^* = \frac{\tilde{\lambda}_j}{\sum_{k=1}^J \tilde{\lambda}_k}. +$$ + +```{code-cell} ipython3 +def create_redistributed_weights(lambda_orig, alpha=0.5, beta=2.0): + """Create more egalitarian Pareto weights via smooth redistribution.""" + lambda_orig = np.asarray(lambda_orig, dtype=float) + J = len(lambda_orig) + if J == 0: + raise ValueError("lambda_orig must be non-empty") + if J == 1: + return np.array([1.0]) + lambda_bar = 1.0 / J + j_indices = np.arange(J) + f_j = j_indices / (J - 1) + dist_from_median = np.abs(f_j - 0.5) / 0.5 # in [0, 1], smallest near the median + tau_j = np.clip(alpha * (dist_from_median ** beta), 0.0, 1.0) + lambda_tilde = lambda_orig + tau_j * (lambda_bar - lambda_orig) + lambda_star = lambda_tilde / lambda_tilde.sum() + return lambda_star +``` + +```{code-cell} ipython3 +paths["u_ratio"] +``` + +```{code-cell} ipython3 +mu_values = paths["u_ratio"] +idx_sorted = np.argsort(-mu_values) +lambda_orig_sorted = mu_values[idx_sorted] + +red_alpha = 0.8 +red_beta = 0.0 + +lambda_star_sorted = create_redistributed_weights(lambda_orig_sorted, alpha=red_alpha, beta=red_beta) + +# Map redistributed weights back to the original household ordering +lambda_star = np.empty_like(mu_values, dtype=float) +lambda_star[idx_sorted] = lambda_star_sorted + +fig, axes = plt.subplots(1, 2, figsize=(14, 5)) + +n_plot = len(lambda_orig_sorted) +axes[0].plot(lambda_orig_sorted[:n_plot], 'o-', label=r'Original $\lambda_j$', alpha=0.7) +axes[0].plot(lambda_star_sorted[:n_plot], 's-', label=r'Redistributed $\lambda_j^*$', alpha=0.7) +axes[0].axhline(1.0 / len(lambda_orig_sorted), color='k', linestyle='--', + label=f'Equal weight (1/{len(lambda_orig_sorted)})', alpha=0.5) +axes[0].set_xlabel(r'Household index $j$ (sorted by $\lambda$)') +axes[0].set_ylabel('Pareto weight') +axes[0].legend() + +delta_lambda_sorted = lambda_star_sorted - lambda_orig_sorted +axes[1].bar(range(n_plot), delta_lambda_sorted[:n_plot], alpha=0.7, + color=['g' if x > 0 else 'r' for x in delta_lambda_sorted[:n_plot]]) +axes[1].axhline(0, color='k', linestyle='-', linewidth=0.8) +axes[1].set_xlabel(r'Household index $j$ (sorted by $\lambda$)') +axes[1].set_ylabel(r'$\lambda_j^* - \lambda_j$') + +plt.tight_layout() +plt.show() +``` + +Let $R = \delta_k + \gamma_1$. For a given Pareto-weight vector $\omega$ (pre: $\omega=\mu$; post: $\omega=\lambda^*$), define household assets +$$ +a_{j,t} \equiv \omega_j k_t + \hat{k}_{j,t}. +$$ + +The income measure we use is +$$ +y_{j,t}(\omega) += \omega_j d_t + (R-1)a_{j,t-1} += \omega_j d_t + (R-1)\big(\omega_j k_{t-1} + \hat{k}_{j,t-1}\big). +$$ + +So +$$ +y^{pre}_{j,t} = y_{j,t}(\mu), +\qquad +y^{post}_{j,t} = y_{j,t}(\lambda^*). +$$ + +```{code-cell} ipython3 +mu_values = np.asarray(paths["u_ratio"]).reshape(-1) +t0 = burn_in + +c_hh = paths["c_j"][:, t0:] +d_hh = paths["d_j"][:, t0:] +a_hh = paths["bonds_j"][:, t0:] +khat_hh = paths["khat_j"][:, t0:] + +chi_hh = paths["chi_tilde"][:, t0:] +c_share = mu_values[:, None] * paths["c"][0, t0:] + +print(f"max|chi_tilde| over panel = {float(np.max(np.abs(chi_hh))):.6g}") +print(f"std(mu*c_agg) = {float(np.std(c_share)):.6g}, std(chi_tilde) = {float(np.std(chi_hh)):.6g}") +resid = c_hh - c_share +var_c = float(np.var(c_hh)) +var_resid = float(np.var(resid)) +print(f"Variance share explained by mu*c_agg (pooled): {1.0 - (var_resid / var_c):.6f}") + +R = float(deltak + gamma1) + +# Aggregate quantities +c_agg = paths["c"][0, t0:] # aggregate consumption +d_agg = paths["d"][0, t0:] # aggregate endowment +k_agg = paths["k"][0, t0:] # aggregate capital + +# Pre-redistribution: income and consumption based on original Pareto weights μ_j +# c_jt = μ_j * c_t + χ̃_jt (Gorman sharing rule) +# y_pre = μ_j * d_t + (R-1) * a_lag (mutual fund income), with a_{j,t} = μ_j k_t + k̂_{j,t} +a_lag = np.concatenate([a_hh[:, [0]], a_hh[:, :-1]], axis=1) +k_lag = np.concatenate([k_agg[[0]], k_agg[:-1]], axis=0)[None, :] # (1, T) +khat_lag = np.concatenate([khat_hh[:, [0]], khat_hh[:, :-1]], axis=1) + +# Consistency check: total assets a_{j,t-1} = μ_j k_{t-1} + k̂_{j,t-1} +a_lag_check = mu_values[:, None] * k_lag + khat_lag +print(f"max|a_lag - (mu*k_lag + khat_lag)|: {float(np.max(np.abs(a_lag - a_lag_check))):.2e}") + +# Post-redistribution: use redistributed Pareto weights λ_j^* +# Sort original weights, redistribute, then map back to original household ordering +idx_sorted = np.argsort(-mu_values) +lambda_orig_sorted = mu_values[idx_sorted] + +# alpha controls redistribution strength beta controls shape +lambda_star_sorted = create_redistributed_weights(lambda_orig_sorted, alpha=red_alpha, beta=red_beta) +lambda_star = np.empty_like(mu_values, dtype=float) +lambda_star[idx_sorted] = lambda_star_sorted + + +# Pre/post allocations implied by weights (hold aggregate paths fixed) +ubs_for_alloc = Ub_list if "Ub_list" in globals() else ubs +h0i_alloc = np.array([[0.0]]) + +pre = allocation_from_weights(paths, econ, ubs_for_alloc, mu_values, gamma1, lam, h0i_alloc) +post = allocation_from_weights(paths, econ, ubs_for_alloc, lambda_star, gamma1, lam, h0i_alloc) + +c_pre = pre["c"][:, t0:] +y_pre = pre["y_net"][:, t0:] + +c_post = post["c"][:, t0:] +y_post = post["y_net"][:, t0:] + +# Balanced transfer (flow): implements the new allocation with aggregate feasibility +T_flow = c_post - c_pre + +# Budget constraint check: savings = income - consumption +# s_t = y_t - c_t should equal change in assets: a_t - a_{t-1}/R (approx) +savings_pre = y_pre - c_pre +savings_post = y_post - c_post + +def _pct(panel, ps=(90, 50, 10)): + return np.percentile(panel, ps, axis=0) + + +# Cross-sectional percentiles over time (drop the first period + leave a tail unplotted) +head_drop = 1 +tail_drop = 50 +T_avail = c_pre.shape[1] +start = min(max(head_drop, 0), T_avail) +end = max(start, T_avail - tail_drop) + +T_ts = min(500, end - start) +t = t0 + start + np.arange(T_ts) + +y_pre_pct = _pct(y_pre[:, start:start + T_ts], ps=(90, 50, 10)) +y_post_pct = _pct(y_post[:, start:start + T_ts], ps=(90, 50, 10)) +c_pre_pct = _pct(c_pre[:, start:start + T_ts], ps=(90, 50, 10)) +c_post_pct = _pct(c_post[:, start:start + T_ts], ps=(90, 50, 10)) + +fig, ax = plt.subplots(1, 3, figsize=(14, 4), sharey=True) +titles = [ + r"Pre-tax income $y^{pre}$", + rf"Post-tax income $y^{{post}}$", + rf"Consumption $c^{{post}}$" +] +panels = [y_pre_pct, y_post_pct, c_post_pct] + +for idx in range(3): + ax[idx].set_title(titles[idx]) + ax[idx].plot(t, panels[idx][0], label="p90") + ax[idx].plot(t, panels[idx][1], label="p50") + ax[idx].plot(t, panels[idx][2], label="p10") + ax[idx].set_xlabel("t") + +plt.legend() + +plt.tight_layout() +plt.show() +``` \ No newline at end of file From 453745ac07c3617c821f15518a0f7d771a30c90d Mon Sep 17 00:00:00 2001 From: Humphrey Yang Date: Wed, 24 Dec 2025 18:58:47 +1100 Subject: [PATCH 02/17] updates --- lectures/gorman_heterogeneous_households.md | 946 +++++++++++++------- 1 file changed, 621 insertions(+), 325 deletions(-) diff --git a/lectures/gorman_heterogeneous_households.md b/lectures/gorman_heterogeneous_households.md index 9f0c8041..c7c286b5 100644 --- a/lectures/gorman_heterogeneous_households.md +++ b/lectures/gorman_heterogeneous_households.md @@ -57,21 +57,11 @@ import matplotlib.pyplot as plt ## Overview -Gorman aggregation lets us solve heterogeneous-household economies in two steps. +Gorman aggregation lets us solve heterogeneous-household economies in two steps: solve a representative-agent linear-quadratic planning problem for aggregates, then recover household allocations via a wealth-share rule with household-specific deviation terms. -First, solve a representative-agent linear-quadratic planning problem to obtain aggregate allocations, shadow prices, and the closed-loop law of motion. +The key insight is that Gorman conditions ensure all consumers have parallel Engel curves, so aggregate allocations and prices can be determined independently of distribution. -Second, recover household-level allocations via a wealth-share rule with a household-specific deviation term. - -The idea behind Gorman aggregation is beautifully simple yet powerful. - -In a general heterogeneous-consumer economy, the set of Pareto optimal allocations depends on how utility is distributed across consumers. - -If we change the aggregate endowment, the new utility possibility frontier may cross the old one, meaning there is no way to rank aggregate allocations independently of distribution. - -Gorman's conditions eliminate this problem: they ensure that all consumers have parallel Engel curves, so the coefficient on wealth in the demand function is the same for everyone. - -This lets us determine aggregate allocations and prices before worrying about how to divide consumption among households. +This eliminates the standard problem where the utility possibility frontier shifts with endowment changes, making it impossible to rank allocations without specifying distributional weights. With the help of this powerful result, we proceed in three steps in this lecture: @@ -81,6 +71,39 @@ With the help of this powerful result, we proceed in three steps in this lecture We then simulate examples with two and many households. +### Gorman aggregation (static) + +To see where the sharing rule comes from, start with a static economy with $n$ goods, price vector $p$, and consumers $j = 1, \ldots, J$. + +Gorman's aggregation conditions amount to assuming each consumer's compensated demand can be written as an affine function of a *scalar* utility index: + +$$ +c^j = \psi_j(p) + u^j \psi_c(p), +$$ + +where $\psi_c(p)$ is common across consumers and $\psi_j(p)$ is consumer-specific. +Aggregating over consumers gives a representative-consumer demand system + +$$ +c^a = \psi_a(p) + u^a \psi_c(p), +\qquad +u^a = \sum_{j=1}^J u^j, +\qquad +\psi_a(p) = \sum_{j=1}^J \psi_j(p). +$$ + +Because the functions involved are homogeneous of degree zero in $p$ (only *relative* prices matter), the implied gradient $p$ can be recovered from the aggregate bundle without knowing how utility is distributed across consumers. +This is why aggregate allocations and prices can be computed *before* household allocations. + +In the quadratic specifications used in this lecture (and in the book), the baseline components are degenerate in the sense that $\psi_j(p) = \chi^j$ is independent of $p$. +In that case, the static sharing rule reduces to + +$$ +c^j - \chi^j = \frac{u^j}{u^a}\,(c^a - \chi^a), +$$ + +which is exactly the form we use below, except that goods are indexed by both dates and states. + ### Notation Time is discrete, $t = 0,1,2,\dots$. @@ -130,11 +153,25 @@ Aggregates are economy-wide totals summed across households: consumption $c_t$, #### Gorman weight -The Gorman weight $\mu_j := u_j/u_a$ is household $j$'s wealth share, where $u_j$ is household $j$'s marginal utility of wealth and $u_a$ is the aggregate. +Following the book's notation, let $u^j$ denote the scalar index in the Gorman demand representation above and define + +$$ +\mu_j := \frac{u^j}{u^a}, +\qquad +u^a := \sum_{i=1}^J u^i. +$$ + +In the quadratic setting used here, we can choose $u^j$ proportional to household $j$'s *time-zero marginal utility of wealth* (the Lagrange multiplier on its intertemporal budget constraint, denoted $\mu_{0j}^w$ in {cite:t}`HS2013`), and then normalize so that the $\mu_j$ sum to one. The weights sum to one: $\sum_j \mu_j = 1$. -Under the Gorman sharing rule, household consumption is +The sharing rule can be written either in the "baseline" form used in the book, + +$$ +c_{jt} - \chi_{jt} = \mu_j (c_t - \chi_t), +$$ + +or, equivalently, in the deviation form used in this lecture, $$ c_{jt} = \mu_j c_t + \tilde{\chi}_{jt}. @@ -142,7 +179,7 @@ $$ The proportional term $\mu_j c_t$ is household $j$'s share of aggregate consumption. -The deviation term $\tilde{\chi}_{jt}$ captures how household $j$'s consumption differs from its proportional share due to preference and endowment heterogeneity. +The deviation term $\tilde{\chi}_{jt}$ captures how household $j$'s consumption differs from its proportional share due to preference heterogeneity (bliss points) and initial conditions, and satisfies $\tilde{\chi}_{jt} = \chi_{jt} - \mu_j \chi_t$. These deviations sum to zero across households: $\sum_j \tilde{\chi}_{jt} = 0$. @@ -836,25 +873,61 @@ def compute_household_paths(econ, U_b_list, U_d_list, x0, x_path, γ_1, Λ, h0i= ``` ```{code-cell} ipython3 -def simulate_state_path(A0, C, x0, ts_length, rng=None): +def solve_model(info, tech, pref, U_b_list, U_d_list, γ_1, Λ, z0, ts_length=2000): """ - Simulate x_{t+1} = A0 x_t + C w_{t+1} with iid standard normal shocks. - """ - rng = np.random.default_rng(rng) - x0 = np.asarray(x0, dtype=float).reshape(-1, 1) - n_x = x0.shape[0] - n_w = np.asarray(C).shape[1] - - x = np.empty((n_x, ts_length)) - w = np.empty((n_w, ts_length)) - x[:, 0] = x0[:, 0] - w[:, 0] = 0.0 + Solve the representative-agent DLE problem and compute household paths. - for t in range(ts_length - 1): - w[:, t + 1] = rng.standard_normal(n_w) - x[:, t + 1] = (A0 @ x[:, t] + C @ w[:, t + 1]).reshape(-1) + Parameters + ---------- + info : tuple + (A_22, C_2, U_b, U_d) - Information structure + tech : tuple + (Φ_c, Φ_g, Φ_i, Γ, δ_k, θ_k) - Technology parameters + pref : tuple + (β, Λ, Π_h, δ_h, θ_h) - Preference parameters + U_b_list : list + List of household-specific bliss matrices + U_d_list : list + List of household-specific endowment matrices + γ_1 : float + Capital productivity parameter + Λ : float or array + Durable service flow parameter + z0 : array + Initial exogenous state (will be augmented to full state) + ts_length : int, optional + Length of simulation (default: 2000) + + Returns + ------- + paths : dict + Dictionary containing household paths + econ : DLE + DLE economy object + """ + econ = DLE(info, tech, pref) - return x, w + # Build full initial state x0 = [h_{-1}, k_{-1}, z0] + z0 = np.asarray(z0, dtype=float).reshape(-1, 1) + n_h = np.atleast_2d(econ.thetah).shape[0] + n_k = np.atleast_2d(econ.thetak).shape[0] + x0_full = np.vstack([np.zeros((n_h, 1)), np.zeros((n_k, 1)), z0]) + + # Solve LQ problem and simulate paths + lq = LQ(econ.Q, econ.R, econ.A, econ.B, + econ.C, N=econ.W, beta=econ.beta) + x_path, _, _ = lq.compute_sequence(x0_full, ts_length=ts_length) + + paths = compute_household_paths( + econ=econ, + U_b_list=U_b_list, + U_d_list=U_d_list, + x0=x0_full, + x_path=x_path, + γ_1=γ_1, + Λ=Λ, + ) + return paths, econ ``` (gorman_twohh)= @@ -920,7 +993,10 @@ We simulate the closed-loop DLE state and compute household paths. x0 = np.array([[0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0]]).T ts_length = 2_000 -x_path, _ = simulate_state_path(econ.A0, econ.C, x0, ts_length, rng=1234) +# Solve LQ problem and simulate paths +lq = LQ(econ.Q, econ.R, econ.A, econ.B, + econ.C, N=econ.W, beta=econ.beta) +x_path, _, _ = lq.compute_sequence(x0, ts_length=ts_length) paths = compute_household_paths( econ=econ, @@ -977,8 +1053,7 @@ plt.show() np.max(np.abs(paths["k_hat"].sum(axis=0))) ``` - -## Many-Household Gorman Economies +## Many-household Gorman economies This section presents a scalable Gorman economy specification that accommodates many households with heterogeneous endowments and preferences. @@ -1166,206 +1241,420 @@ def build_reverse_engineered_gorman_extended( return A22, C2, Ub, Ud, Ub_per_house, Ud_per_house, x0 ``` -### Many-Household Example +### 100-household reverse engineered economy We now instantiate a 100-household economy using the reverse-engineered Gorman specification. +We use the same technology and preference parameters as the two-household example. + +```{code-cell} ipython3 +# Technology and preference parameters (same as two-household example) +ϕ_1 = 1e-5 +γ_1 = 0.1 +δ_k = 0.95 +β = 1.0 / (γ_1 + δ_k) + +θ_k = 1.0 +δ_h = 0.2 +θ_h = 0.1 +Λ = 0.0 +Π_h = 1.0 + +Φ_c = np.array([[1.0], [0.0]]) +Φ_g = np.array([[0.0], [1.0]]) +Φ_i = np.array([[1.0], [-ϕ_1]]) +Γ = np.array([[γ_1], [0.0]]) +``` + ```{code-cell} ipython3 np.random.seed(42) N = 100 -# AR(2) parameters for aggregate endowment -# rho1 = 1.3435 -# rho2 = -0.9025 -rho1 = 0.95 -rho2 = 0.0 - -sigma_a = 0.5 +ρ1 = 0.95 +ρ2 = 0.0 +σ_a = 0.5 -# Individual endowment parameters -# alphas: intercepts (drawn randomly for each household) -alphas = np.random.uniform(3.0, 5.0, N) +αs = np.random.uniform(3.0, 5.0, N) -# phis: loadings on aggregate endowment (must sum to 1) -phis_raw = np.random.uniform(0.5, 1.5, N) -phis = phis_raw / np.sum(phis_raw) # normalize to sum to 1 +φs_raw = np.random.uniform(0.5, 1.5, N) +φs = φs_raw / np.sum(φs_raw) -wealth_rank_proxy = np.argsort(np.argsort(alphas)) # 0 = lowest alpha, N-1 = highest -wealth_pct_proxy = (wealth_rank_proxy + 0.5) / N # in (0, 1) -poorness = 1.0 - wealth_pct_proxy # 1 = poorest, 0 = richest +wealth_rank_proxy = np.argsort(np.argsort(αs)) +wealth_pct_proxy = (wealth_rank_proxy + 0.5) / N +poorness = 1.0 - wealth_pct_proxy -# Number of households that absorb idiosyncratic shocks (Gorman requirement) -# Must be defined BEFORE creating rho_idio array n_absorb = 50 -# sigmas: idiosyncratic endowment shock scales -# Each household has a sigma, but only households n_absorb+1..N have their own eta state -sigma_idio_min, sigma_idio_max = 0.2, 5.0 -sigmas = sigma_idio_min + (sigma_idio_max - sigma_idio_min) * (poorness ** 2.0) +σ_idio_min, σ_idio_max = 0.2, 5.0 +σs = σ_idio_min + (σ_idio_max - σ_idio_min) * (poorness ** 2.0) -# rho_idio: persistence for eta_{n_absorb+1..N} -# Only households n_absorb+1 through N have independent idiosyncratic shocks -rho_idio_min, rho_idio_max = 0.0, 0.98 -rho_idio = rho_idio_min + (rho_idio_max - rho_idio_min) * (poorness[n_absorb:] ** 1.0) +ρ_idio_min, ρ_idio_max = 0.0, 0.98 +ρ_idio = ρ_idio_min + (ρ_idio_max - ρ_idio_min) * (poorness[n_absorb:] ** 1.0) -# Preference parameters b_bar = 5.0 -# Idiosyncratic preference shocks (turn on to generate household-specific consumption movements) -enable_pref_shocks = False # ENABLE to see time-varying consumption heterogeneity -pref_shock_scale = 0.5 # Increase scale to see clearer effects +enable_pref_shocks = False +pref_shock_scale = 0.5 pref_shock_persistence = 0.7 if enable_pref_shocks: - gammas_pref = pref_shock_scale * np.ones(N) - rho_pref = pref_shock_persistence + γs_pref = pref_shock_scale * np.ones(N) + ρ_pref = pref_shock_persistence else: - gammas_pref = np.zeros(N) # mute preference shocks; shock endowment only - rho_pref = 0.0 # keep preference states deterministic at 0 + γs_pref = np.zeros(N) + ρ_pref = 0.0 -# Burn in for the aggregate and individual series burn_in = 200 -# Build the economy A22, C2, Ub, Ud, Ub_list, Ud_list, x0 = build_reverse_engineered_gorman_extended( n=N, - rho1=rho1, rho2=rho2, sigma_a=sigma_a, - alphas=alphas, phis=phis, sigmas=sigmas, - b_bar=b_bar, gammas=gammas_pref, - rho_idio=rho_idio, rho_pref=rho_pref, + rho1=ρ1, rho2=ρ2, sigma_a=σ_a, + alphas=αs, phis=φs, sigmas=σs, + b_bar=b_bar, gammas=γs_pref, + rho_idio=ρ_idio, rho_pref=ρ_pref, n_absorb=n_absorb, ) print(f"State dimension nz = {A22.shape[0]}") print(f"Shock dimension nw = {C2.shape[1]}") -print(f"sum(phis) = {np.sum(phis):.6f} (should be 1.0)") +print(f"sum(φs) = {np.sum(φs):.6f} (should be 1.0)") ``` ```{code-cell} ipython3 -# Initialize economy via DLE for the reverse-engineered Gorman case. info_ar1 = (A22, C2, Ub, Ud) -pref_ar1 = (np.array([[beta]]), - np.array([[lam]]), - np.array([[pih]]), - np.array([[deltah]]), - np.array([[thetah]])) -tech_ar1 = (phic, phig, phii, gamma, - np.array([[deltak]]), - np.array([[thetak]])) +pref_ar1 = (np.array([[β]]), + np.array([[Λ]]), + np.array([[Π_h]]), + np.array([[δ_h]]), + np.array([[θ_h]])) +tech_ar1 = (Φ_c, Φ_g, Φ_i, Γ, + np.array([[δ_k]]), + np.array([[θ_k]])) paths, econ = solve_model(info_ar1, tech_ar1, pref_ar1, - Ub_list, Ud_list, gamma1, lam, z0=x0) + Ub_list, Ud_list, γ_1, Λ, z0=x0) ``` -The following figure shows household-level consumption, endowments, and bond positions. - ```{code-cell} ipython3 -T = 50 +print(f"State dimension: {A22.shape[0]}, shock dimension: {C2.shape[1]}") +print(f"Aggregate: ρ₁={ρ1:.3f}, ρ₂={ρ2:.3f}, σₐ={σ_a:.2f}") +print(f"Endowments: α ∈ [{np.min(αs):.2f}, {np.max(αs):.2f}], Σφ={np.sum(φs):.6f}") +``` -plt.figure(figsize=(14, 8)) +The next plots show household consumption and dividend paths after discarding the initial burn-in period. -# Individual consumption paths -plt.subplot(2, 2, 1) -plt.title('Consumption (N households)') -plt.plot(paths["c_j"][:, 1:T].T) +```{code-cell} ipython3 +T_plot = 50 -# Individual dividends -plt.subplot(2, 2, 2) -plt.title('Dividends (N households)') -plt.plot(paths["d_j"][:, 1:T].T) +fig, axes = plt.subplots(1, 2, figsize=(14, 4)) -# Bond purchases k̂_jt -plt.subplot(2, 2, 3) -plt.title(r'Bond purchases $\hat{k}_{jt}$') -plt.plot(paths["khat_j"][:, :T].T) +axes[0].plot(paths["c_j"][:, burn_in:burn_in+T_plot].T, lw=2) +axes[0].set_xlabel("time") +axes[0].set_ylabel("consumption") -# Total bonds -plt.subplot(2, 2, 4) -plt.title('Total Bonds $b_{jt}$') -plt.plot(paths["bonds_j"][:, :T].T) +axes[1].plot(paths["d_share"][:, burn_in:burn_in+T_plot].T, lw=2) +axes[1].set_xlabel("time") +axes[1].set_ylabel("dividends") plt.tight_layout() plt.show() ``` -## State-Space Representation +## State-space representation -This section connects the Gorman economy to its underlying linear state-space representation. +### Closed-loop state-space system -### Closed-Loop Dynamics +The DLE framework represents the economy as a linear state-space system. After solving the optimal control problem and substituting the policy rule, we obtain a closed-loop system: -The aggregate planning problem has a linear-quadratic structure. Following Chapter 5 of {cite:t}`HS2013`, the state and control are +$$ +x_{t+1} = A_0 x_t + C w_{t+1}, +$$ + +where the aggregate state vector is $$ x_t = \begin{bmatrix} -h_{t-1} \\ -k_{t-1} \\ +h_{t-1}\\ +k_{t-1}\\ z_t \end{bmatrix}, -\qquad -u_t := i_t, -\qquad -z_{t+1} = A_{22} z_t + C_2 w_{t+1}, $$ -where $h_{t-1}$ is the household service stock, $k_{t-1}$ is capital, and $z_t$ stacks exogenous variables. +with $h_{t-1}$ the household service stock, $k_{t-1}$ the capital stock, and $z_t$ the exogenous state (constant, aggregate endowment states, and idiosyncratic shock states). -The controlled transition is +Any equilibrium quantity is a linear function of the state. The `quantecon.DLE` module provides selection matrices $S_\bullet$ such that: $$ -x_{t+1} = A x_t + B u_t + C w_{t+1}. +c_t = S_c x_t,\quad g_t = S_g x_t,\quad i_t = S_i x_t,\quad h_t = S_h x_t,\quad k_t = S_k x_t,\quad d_t = S_d x_t,\quad b_t = S_b x_t,\quad s_t = S_s x_t. $$ -Solving the LQ problem yields optimal feedback $u_t = -F x_t$, giving the closed-loop law of motion +We can stack these to form a measurement matrix: $$ -x_{t+1} = A_0 x_t + C w_{t+1}, \qquad A_0 := A - BF. +G = +\begin{bmatrix} +S_c\\ S_g\\ S_i\\ S_h\\ S_k\\ S_d\\ S_b\\ S_s +\end{bmatrix}, +\qquad\text{giving}\qquad +y_t = G x_t. $$ -### Selection Matrices +The simulated state path is stored in `paths["x_path"]`, where `x_path[:, t]` corresponds to $x_t$. -All equilibrium quantities are linear functions of the state. The `quantecon.DLE` class provides selection matrices $S_\bullet$ such that +```{code-cell} ipython3 +A0 = econ.A0 +C = econ.C -$$ -c_t = S_c x_t, \quad i_t = S_i x_t, \quad k_t = S_k x_t, \quad d_t = S_d x_t, \quad \text{etc.} -$$ +G = np.vstack([ + econ.Sc, + econ.Sg, + econ.Si, + econ.Sh, + econ.Sk, + econ.Sd, + econ.Sb, + econ.Ss, +]) + +print(f"Shapes: A0 {A0.shape}, C {C.shape}, G {G.shape}") +print(f"max |A0[2:,2:] - A22| = {np.max(np.abs(A0[2:, 2:] - A22)):.2e}") +``` -Stacking these gives a measurement equation $y_t = G x_t$ where $G$ collects the relevant selection matrices. +```{code-cell} ipython3 +A0[:2, :2] +``` + +### Impulse responses + +We compute impulse responses to show how shocks propagate through the economy. + +**Methodology**: The closed-loop state evolves as $x_{t+1} = A_0 x_t + C w_{t+1}$ where $w_{t+1}$ are i.i.d. shocks. + +To trace an impulse response: +1. Set $x_0 = C e_j \times \sigma$ where $e_j$ is the $j$-th standard basis vector and $\sigma$ is the shock size +2. Iterate forward with $x_{t+1} = A_0 x_t$ (no further shocks) +3. Compute observables via measurement equation: $y_t = G x_t$ ```{code-cell} ipython3 -# Closed-loop state space matrices -A0 = econ.A0 -C = econ.C +def compute_irf(A0, C, G, shock_idx, T=50, shock_size=1.0): + """Compute impulse response to shock shock_idx with given shock size.""" + n_x = A0.shape[0] + n_w = C.shape[1] + + x_path = np.zeros((n_x, T)) + y_path = np.zeros((G.shape[0], T)) + + w0 = np.zeros(n_w) + w0[shock_idx] = shock_size + x_path[:, 0] = C @ w0 + y_path[:, 0] = G @ x_path[:, 0] + + for t in range(1, T): + x_path[:, t] = A0 @ x_path[:, t-1] + y_path[:, t] = G @ x_path[:, t] + + return x_path, y_path + +n_h = np.atleast_2d(econ.thetah).shape[0] +n_k = np.atleast_2d(econ.thetak).shape[0] + +print(f"Diagnostics:") +print(f" State dimension: {A0.shape[0]}") +print(f" n_h={n_h}, n_k={n_k}") +print(f" x_t = [h_{{-1}}, k_{{-1}}, z_t]") +print(f" z_t = [const, d_{{a,t}}, d_{{a,t-1}}, ...]") +print(f"\nShock loading C shape: {C.shape}") +print(f" Shock 0 impact on first 10 states:") +for i in range(min(10, C.shape[0])): + if abs(C[i, 0]) > 1e-10: + print(f" State {i}: {C[i, 0]:.4f}") + +idx_da = n_h + n_k + 1 +print(f"\nd_{{a,t}} should be at state index {idx_da}") +print(f" C[{idx_da}, 0] = {C[idx_da, 0]:.6f}") + +T_irf = 50 +shock_size = 1.0 +irf_x, irf_y = compute_irf(A0, C, G, shock_idx=0, T=T_irf, shock_size=shock_size) + +idx_c = 0 +idx_g = 1 +idx_i = 2 +idx_k = 4 + +print(f"\nImpact of unit shock on state d_{{a,t}}: {irf_x[idx_da, 0]:.6f}") +print(f"Impact on consumption: {irf_y[idx_c, 0]:.6f}") +print(f"Impact on investment: {irf_y[idx_i, 0]:.6f}") +print(f"Impact on capital: {irf_y[idx_k, 0]:.6f}") + +da_irf = irf_x[idx_da, :] + +fig, axes = plt.subplots(1, 2, figsize=(14, 4)) + -print(f"State dimension: {A0.shape[0]}") -print(f"Shock dimension: {C.shape[1]}") +axes[0].plot(irf_y[idx_k, :], lw=2) +axes[0].set_xlabel('time') +axes[0].set_ylabel('capital') -# Dominant eigenvalues of the closed-loop dynamics -eigvals = np.linalg.eigvals(A0) -print(f"Largest eigenvalues: {np.sort(np.abs(eigvals))[-3:][::-1]}") +axes[1].plot(da_irf, lw=2) +axes[1].set_xlabel('time') +axes[1].set_ylabel(r'$d_{a,t}$ (aggregate endowment shock)') + + +plt.tight_layout() +plt.show() ``` -The following figure shows selected components of the aggregate state $x_t$. +**Interpreting the results**: + +The diagnostics above show which state variables are hit by the aggregate shock and the resulting propagation through the economy. + +If $d_{a,t}$ shows clear AR(2) dynamics, this confirms the aggregate endowment shock propagates correctly through the closed-loop system. + +The responses in consumption, investment, and capital reflect the planner's optimal smoothing behavior given the Hall-style preferences with adjustment costs. ```{code-cell} ipython3 -xp = paths["xp"] -T_plot = 250 +# Set time offset for DMD analysis (after burn-in period) +t0 = burn_in + +# Now stack the household consumption panel with household endowments and rerun DMD. +c_j_t0 = paths["c_j"][..., t0:] +d_j_t0 = paths["d_share"][..., t0:] + +c_panel = np.asarray(c_j_t0 - c_j_t0.mean(axis=0)) # (N, T - t0) +d_panel = np.asarray(d_j_t0 - d_j_t0.mean(axis=0)) # (N, T - t0) +assert c_panel.shape == d_panel.shape + +# Plot aggregate endowment and individual household endowments +d_agg = paths["d"][0, t0:] +d_households = d_j_t0 + +T_plot = min(500, d_agg.shape[0]) +time_idx = np.arange(T_plot) +n_to_plot = 1 + + +fig, axes = plt.subplots(2, 1, figsize=(14, 8)) + +# Top panel: Aggregate endowment +axes[0].plot(time_idx, d_agg[:T_plot], linewidth=2.5, color='C0', label='Aggregate endowment $d_t$') +axes[0].set_ylabel('Endowment') +axes[0].set_title('Aggregate Endowment (contains ghost AR(2) process)') +axes[0].legend() + +# Also plot the mean across households +d_mean = d_households[:, :T_plot].mean(axis=0) +axes[1].plot(time_idx, d_mean, linewidth=2.5, color='black', linestyle='--', + label=f'Mean across {d_households.shape[0]} households', alpha=0.8) + +axes[1].set_xlabel('Time (after burn-in)') +axes[1].set_ylabel('Endowment') +axes[1].set_title(f'Average of Individual Household Endowments') +axes[1].legend(loc='upper right', ncol=2) -plt.figure(figsize=(10, 4)) -plt.plot(xp[1, :T_plot], label=r"$k_{t-1}$ (capital)") -plt.plot(xp[3, :T_plot], label=r"$d_{a,t}$ (aggregate endowment)") -plt.xlabel("t") -plt.legend() plt.tight_layout() plt.show() ``` +```{code-cell} ipython3 +def make_state_labels(n, n_absorb=None, latex=False): + """Create labels for state vector x_t = [h_{t-1}, k_{t-1}, z_t].""" + if n_absorb is None: + n_absorb = max(1, n // 10) + + if latex: + base = ["$h_{t-1}$", "$k_{t-1}$", "const", "$d_{a,t}$", "$d_{a,t-1}$"] + etas = [fr"$\eta_{{{j+n_absorb+1}}}$" for j in range(n - n_absorb)] + xis = [fr"$\xi_{{{j+1}}}$" for j in range(n)] + return base + etas + xis + + base = ["h_{t-1}", "k_{t-1}", "const", "d_a,t", "d_a,t-1"] + etas = [f"eta_{j+n_absorb+1}" for j in range(n - n_absorb)] + xis = [f"xi_{j+1}" for j in range(n)] + return base + etas + xis + +Sc = econ.Sc +sc = Sc.reshape(-1) +nx = sc.size + +x_labels = make_state_labels(N, n_absorb=n_absorb) + +tol = 1e-12 +nz_idx = np.where(np.abs(sc) > tol)[0] +print(f"c_t = Sc x_t has {len(nz_idx)} nonzero coefficients (out of {nx})") + +topk = 15 +top_idx = nz_idx[np.argsort(np.abs(sc[nz_idx]))[::-1][:topk]] +print("\nTop |Sc| coefficients:") +for idx in top_idx: + print(f" {x_labels[idx]:>20s}: {sc[idx]: .6g}") + +idx_eta_start = 5 +idx_eta_end = 5 + (N - n_absorb) +idx_xi_start = idx_eta_end +idx_xi_end = idx_xi_start + N + +eta_coef = sc[idx_eta_start:idx_eta_end] +xi_coef = sc[idx_xi_start:idx_xi_end] + +print(f"\nIdiosyncratic endowment shocks: min={np.min(eta_coef):.3g}, max={np.max(eta_coef):.3g}") +print(f"Preference shocks: min={np.min(xi_coef):.3g}, max={np.max(xi_coef):.3g}") + +xp_trim = paths["x_path"][:, burn_in:] +c_path = (Sc @ xp_trim).squeeze() +c_dm = c_path - c_path.mean() + +def r2_from_state_indices(idxs): + Xg = xp_trim[idxs, :].T + Xg = Xg - Xg.mean(axis=0, keepdims=True) + y = c_dm + beta_hat = np.linalg.lstsq(Xg, y, rcond=None)[0] + y_hat = Xg @ beta_hat + resid = y - y_hat + return 1 - (np.var(resid) / np.var(y)) + +groups = { + "capital only (k_{t-1})": [1], + "aggregate endow (d_a,t, d_a,t-1)": [3, 4], + "idio endow only (all eta_j)": list(range(idx_eta_start, idx_eta_end)), + "pref shocks only (all xi_j)": list(range(idx_xi_start, idx_xi_end)), + "all z_t": list(range(2, nx)), +} + +print("\nR^2 of c_t (demeaned) explained by state blocks:") +for name, idxs in groups.items(): + print(f" {name:<35s}: {r2_from_state_indices(idxs):.4f}") +``` + +```{code-cell} ipython3 +print( +econ.Sc.shape, # c_t +econ.Sg.shape, # g_t +econ.Si.shape, # i_t +econ.Sh.shape, # h_t +econ.Sk.shape # k_t +) +``` + +```{code-cell} ipython3 +xp = paths["x_path"] +T_plot2 = 250 + +fig, ax = plt.subplots(figsize=(10, 6)) +ax.plot(xp[1, burn_in:burn_in+T_plot2], label=r"$k_{t-1}$", lw=2) +if xp.shape[0] > 3: + ax.plot(xp[3, burn_in:burn_in+T_plot2], label=r"$d_{0, t}$", lw=2) +ax.set_xlabel("time") +ax.legend() +plt.tight_layout() +plt.show() +``` -## Redistribution via Pareto Weight Reallocation +## Redistribution via Pareto weight reallocation -This section develops a framework for analyzing the distributional effects of tax-and-transfer schemes within the Gorman aggregation model. The key insight is that we can reinterpret the competitive equilibrium consumption allocation in terms of Pareto weights, then consider alternative weight distributions that implement redistribution while preserving aggregate consumption and capital accumulation. +This section analyzes tax-and-transfer schemes by reinterpreting competitive equilibrium allocations in terms of Pareto weights, then considering alternative weight distributions that redistribute consumption while preserving aggregate dynamics. -### Competitive Equilibrium and Pareto Weights +### Competitive equilibrium and Pareto weights Start with the competitive equilibrium consumption allocation described by equation (12.4.4) on page 264 of Hansen and Sargent (2013): @@ -1379,33 +1668,19 @@ where: - The $j$ subscript pertains to the $j$th household - $c_{jt}$ and $\chi_{jt}$ are computed via the Gorman aggregation framework (the `heter` function above) -Recall from {eq}`eq:sharing_rule` that the Gorman sharing rule gives $c_{jt} = \mu_j c_t + \tilde{\chi}_{jt}$ where $\mu_j := u_j/u_a$ is household $j$'s wealth share. This $\mu_j$ directly corresponds to the implicit Pareto weight attached to individual $j$ in the competitive equilibrium. - -Define the Pareto weight vector: - -$$ -\lambda_j := \frac{u_j}{u_a} = \mu_j, -$$ - -where the Pareto weights are determined by the initial distribution of wealth (the assignment of $\{k_{j,-1}\}_j$ and the endowment processes $\{d_{jt}\}_j$ across households $j \in \{1, \ldots, J\}$). - -A fundamental property of these weights is that they sum to unity: +The Gorman sharing rule gives $c_{jt} = \mu_j c_t + \tilde{\chi}_{jt}$ where $\mu_j := u_j/u_a$ is household $j$'s wealth share. -$$ -\sum_{j=1}^J \lambda_j = \sum_{j=1}^J \frac{u_j}{u_a} = 1. -$$ +Define the Pareto weight $\lambda_j := \mu_j$, determined by initial wealth distribution. -This follows from the construction of the representative agent, whose utility weight $u_a$ is the sum of individual household utility weights (see equation {eq}`eq:agg_preference_aggregates`). +These weights sum to unity: $\sum_{j=1}^J \lambda_j = 1$, following from the representative agent construction. -Using the Pareto weight notation, we can rewrite equation (1) as: +With this notation, household consumption net of deviation terms is proportional to aggregate consumption: $$ c_{jt} - \chi_{jt} = \lambda_j (c_{at} - \chi_{at}). $$ -This expression clarifies that household $j$'s consumption net of the deviation term is proportional to aggregate consumption net of its deviation, with the proportion determined by the Pareto weight $\lambda_j$. - -### Redistribution Proposal +### Redistribution proposal Consider an efficient tax-and-transfer scheme that leaves aggregate consumption and capital accumulation unaltered but redistributes incomes in a way consistent with a new set of Pareto weights $\{\lambda_j^*\}_{j=1}^J$ satisfying: @@ -1421,132 +1696,189 @@ $$ Since the aggregate allocation $(c_{at}, k_{at}, \chi_{at})$ is unchanged, this redistribution preserves efficiency while reallocating consumption across households according to the new weights. -### Post-Tax-and-Transfer Consumption and Income - -In what follows we treat the model’s household consumption panel $\{c_{jt}\}$ as our “CEX-like” consumption measure. +### Post-tax-and-transfer consumption and income -For income, recall from Section 12.6 (equation {eq}`eq:lm_budget`) that in the limited-markets implementation, household $j$'s budget satisfies: - -$$ -c_{jt} - \chi_{jt} + \mu_j \chi_{at} + \mu_j k_t = \mu_j[(\delta_k + \gamma) k_{t-1} + d_t]. -$$ +We construct household private income from endowments and net asset returns. -In this Hall-style calibration, a share-based “income” concept like $\lambda_j^*\big[(\delta_k + \gamma - 1)k_{t-1}+d_t\big]$ tends to move almost one-for-one with $\lambda_j^* c_t$ because aggregate investment is tiny and the resource constraint implies $c_t \approx (\delta_k + \gamma - 1)k_{t-1}+d_t$. +Let $R := \delta_k + \gamma$ and $a_{j,t}$ denote household $j$'s total assets. -To get an income panel with idiosyncratic risk (closer to what’s used empirically), we instead construct household private income from (i) household endowments and (ii) net asset income from the limited-markets asset position. Let $R := \delta_k + \gamma$ be the gross return and let $a_{j,t}$ denote household $j$’s total asset position (in code: `paths["bonds_j"]`). Define +Define pre-tax income as: $$ y^{pre}_{j,t} := d_{j,t} + (R-1)a_{j,t-1}. $$ -We then define disposable (post-tax-and-transfer) income via a balanced-budget tax-and-transfer rule that shrinks idiosyncratic variation by a factor $(1-\tau)$ while holding aggregate income fixed: - -$$ -y^{post}_{j,t} := (1-\tau) y^{pre}_{j,t} + \tau\,\bar y^{pre}_t, \qquad \bar y^{pre}_t := \frac{1}{J}\sum_{j=1}^J y^{pre}_{j,t}. -$$ - -Here $\tau \in [0,1]$ indexes redistribution/insurance intensity: $\tau=0$ is no redistribution, while $\tau=1$ fully pools income cross-sectionally each date. - -### Pre-Tax Private Income - -We compare consumption to $y^{pre}_{j,t}$ and $y^{post}_{j,t}$ using standard “consumption insurance” metrics (pass-through of idiosyncratic income shocks to idiosyncratic consumption) and percentile plots. - +We compare consumption to income using percentile plots. -The difference between equations (6)-(7) and equations (8)-(9) captures the redistributive impact of the tax-and-transfer system, measured by the change from $\lambda_j$ to $\lambda_j^*$. +To implement redistribution, we construct new Pareto weights $\{\lambda_j^*\}_{j=1}^J$ using a smooth transformation that shifts weight from high-wealth to low-wealth households while preserving $\sum_{j=1}^J \lambda_j^* = 1$. -To implement a more egalitarian distribution, we construct new Pareto weights $\{\lambda_j^*\}_{j=1}^J$ that: - -1. Lower the weights for low-$j$ types (wealthier households in our ordering) -2. Increase the weights for high-$j$ types (less wealthy households) -3. Leave middle-$j$ types relatively unaffected -4. Preserve the constraint $\sum_{j=1}^J \lambda_j^* = 1$ - -We implement this using a smooth transformation. Let $\{\lambda_j\}_{j=1}^J$ denote the original competitive equilibrium Pareto weights (sorted in descending order). Define the redistribution function: - -$$ -f(j; J) = \frac{j-1}{J-1}, \qquad -g(j; J) = 2\left|f(j; J) - \frac{1}{2}\right|, \qquad -\tau(j; J, \alpha, \beta) = \alpha \cdot \left[g(j; J)\right]^\beta, -$$ - -where: -- $j \in \{1, \ldots, J\}$ is the household index -- $\alpha > 0$ controls the overall magnitude of redistribution (with $\tau$ maximized at the extremes) -- $\beta > 1$ controls the progressivity (higher $\beta$ concentrates redistribution more strongly in the tails) - -The redistributed Pareto weights are: +Define the redistribution function: $$ -\tilde{\lambda}_j = \lambda_j + \tau(j; J, \alpha, \beta) \cdot (\bar{\lambda} - \lambda_j), +\tau(j; \alpha, \beta) = \alpha \cdot \left[2\left|\frac{j-1}{J-1} - \frac{1}{2}\right|\right]^\beta, $$ -where $\bar{\lambda} = J^{-1}$ is the equal-weight benchmark. +where $\alpha$ controls magnitude and $\beta$ controls progressivity. -To ensure the weights sum to unity, we normalize: +The redistributed weights are: $$ -\lambda_j^* = \frac{\tilde{\lambda}_j}{\sum_{k=1}^J \tilde{\lambda}_k}. +\lambda_j^* = \frac{\lambda_j + \tau(j) \cdot (J^{-1} - \lambda_j)}{\sum_{k} [\lambda_k + \tau(k) \cdot (J^{-1} - \lambda_k)]}. $$ ```{code-cell} ipython3 -def create_redistributed_weights(lambda_orig, alpha=0.5, beta=2.0): +def create_redistributed_weights(λ_orig, α=0.5, β=2.0): """Create more egalitarian Pareto weights via smooth redistribution.""" - lambda_orig = np.asarray(lambda_orig, dtype=float) - J = len(lambda_orig) + λ_orig = np.asarray(λ_orig, dtype=float) + J = len(λ_orig) if J == 0: - raise ValueError("lambda_orig must be non-empty") + raise ValueError("λ_orig must be non-empty") if J == 1: return np.array([1.0]) - lambda_bar = 1.0 / J + λ_bar = 1.0 / J j_indices = np.arange(J) f_j = j_indices / (J - 1) dist_from_median = np.abs(f_j - 0.5) / 0.5 # in [0, 1], smallest near the median - tau_j = np.clip(alpha * (dist_from_median ** beta), 0.0, 1.0) - lambda_tilde = lambda_orig + tau_j * (lambda_bar - lambda_orig) - lambda_star = lambda_tilde / lambda_tilde.sum() - return lambda_star + τ_j = np.clip(α * (dist_from_median ** β), 0.0, 1.0) + λ_tilde = λ_orig + τ_j * (λ_bar - λ_orig) + λ_star = λ_tilde / λ_tilde.sum() + return λ_star ``` ```{code-cell} ipython3 -paths["u_ratio"] +paths["μ"] ``` ```{code-cell} ipython3 -mu_values = paths["u_ratio"] -idx_sorted = np.argsort(-mu_values) -lambda_orig_sorted = mu_values[idx_sorted] +μ_values = paths["μ"] +idx_sorted = np.argsort(-μ_values) +λ_orig_sorted = μ_values[idx_sorted] -red_alpha = 0.8 -red_beta = 0.0 +red_α = 0.8 +red_β = 0.0 -lambda_star_sorted = create_redistributed_weights(lambda_orig_sorted, alpha=red_alpha, beta=red_beta) +λ_star_sorted = create_redistributed_weights(λ_orig_sorted, α=red_α, β=red_β) # Map redistributed weights back to the original household ordering -lambda_star = np.empty_like(mu_values, dtype=float) -lambda_star[idx_sorted] = lambda_star_sorted +λ_star = np.empty_like(μ_values, dtype=float) +λ_star[idx_sorted] = λ_star_sorted fig, axes = plt.subplots(1, 2, figsize=(14, 5)) -n_plot = len(lambda_orig_sorted) -axes[0].plot(lambda_orig_sorted[:n_plot], 'o-', label=r'Original $\lambda_j$', alpha=0.7) -axes[0].plot(lambda_star_sorted[:n_plot], 's-', label=r'Redistributed $\lambda_j^*$', alpha=0.7) -axes[0].axhline(1.0 / len(lambda_orig_sorted), color='k', linestyle='--', - label=f'Equal weight (1/{len(lambda_orig_sorted)})', alpha=0.5) -axes[0].set_xlabel(r'Household index $j$ (sorted by $\lambda$)') +n_plot = len(λ_orig_sorted) +axes[0].plot(λ_orig_sorted[:n_plot], 'o-', label=r'original $\lambda_j$', alpha=0.7, lw=2) +axes[0].plot(λ_star_sorted[:n_plot], 's-', label=r'redistributed $\lambda_j^*$', alpha=0.7, lw=2) +axes[0].axhline(1.0 / len(λ_orig_sorted), color='k', linestyle='--', + label=f'equal weight (1/{len(λ_orig_sorted)})', alpha=0.5, lw=2) +axes[0].set_xlabel(r'household index $j$ (sorted by $\lambda$)') axes[0].set_ylabel('Pareto weight') axes[0].legend() -delta_lambda_sorted = lambda_star_sorted - lambda_orig_sorted -axes[1].bar(range(n_plot), delta_lambda_sorted[:n_plot], alpha=0.7, - color=['g' if x > 0 else 'r' for x in delta_lambda_sorted[:n_plot]]) -axes[1].axhline(0, color='k', linestyle='-', linewidth=0.8) -axes[1].set_xlabel(r'Household index $j$ (sorted by $\lambda$)') +Δλ_sorted = λ_star_sorted - λ_orig_sorted +axes[1].bar(range(n_plot), Δλ_sorted[:n_plot], alpha=0.7, + color=['g' if x > 0 else 'r' for x in Δλ_sorted[:n_plot]]) +axes[1].axhline(0, color='k', linestyle='-', lw=2) +axes[1].set_xlabel(r'household index $j$ (sorted by $\lambda$)') axes[1].set_ylabel(r'$\lambda_j^* - \lambda_j$') plt.tight_layout() plt.show() ``` +```{code-cell} ipython3 +def allocation_from_weights(paths, econ, U_b_list, weights, γ_1, Λ, h0i=None): + """ + Compute household consumption and income under given Pareto weights. + + Parameters + ---------- + paths : dict + Dictionary containing aggregate paths (x_path, c, d, k) + econ : DLE + DLE economy object + U_b_list : list + List of household-specific bliss matrices + weights : array + Pareto weight vector for households + γ_1 : float + Capital productivity parameter + Λ : float or array + Durable service flow parameter + h0i : array, optional + Initial household durable stock + + Returns + ------- + dict + Dictionary with household consumption 'c' and net income 'y_net' + """ + weights = np.asarray(weights).reshape(-1) + N = len(weights) + + # Extract aggregate paths + x_path = paths["x_path"] + c_agg = paths["c"] + d_agg = paths["d"] + k_agg = paths["k"] + z_path = paths["z_path"] + + _, T = c_agg.shape + + # Get parameters + Θ_h = np.atleast_2d(econ.thetah) + Δ_h = np.atleast_2d(econ.deltah) + Π_h = np.atleast_2d(econ.pih) + Λ = np.atleast_2d(Λ) + n_h = Θ_h.shape[0] + + δ_k = float(np.asarray(econ.deltak).squeeze()) + R = δ_k + float(γ_1) + + Π_inv = np.linalg.inv(Π_h) + A_h = Δ_h - Θ_h @ Π_inv @ Λ + B_h = Θ_h @ Π_inv + + if h0i is None: + h0i = np.zeros((n_h, 1)) + + # Compute household allocations + c_j = np.zeros((N, T)) + χ_tilde = np.zeros((N, T)) + k_hat = np.zeros((N, T)) + + for j in range(N): + U_bj = np.asarray(U_b_list[j], dtype=float) + b_agg = econ.Sb @ x_path + b_tilde = U_bj @ z_path - weights[j] * b_agg + + η = np.zeros((n_h, T + 1)) + η[:, 0] = np.asarray(h0i).reshape(-1) + + for t in range(1, T): + χ_tilde[j, t] = (-Π_inv @ Λ @ η[:, t - 1] + Π_inv @ b_tilde[:, t]).squeeze() + η[:, t] = (A_h @ η[:, t - 1] + B_h @ b_tilde[:, t]).squeeze() + + c_j[j] = (weights[j] * c_agg[0] + χ_tilde[j]).squeeze() + + # Compute bond position + if abs(R - 1.0) >= 1e-14: + k_hat[j, -1] = χ_tilde[j, -1] / (R - 1.0) + for t in range(T - 1, 0, -1): + k_hat[j, t - 1] = (k_hat[j, t] + χ_tilde[j, t]) / R + + # Compute income + k_share = weights[:, None] * k_agg[0, :] + a_total = k_share + k_hat + + # Lagged assets + a_lag = np.concatenate([a_total[:, [0]], a_total[:, :-1]], axis=1) + + # Net income: dividend share + asset return + y_net = weights[:, None] * d_agg[0, :] + (R - 1) * a_lag + + return {"c": c_j, "y_net": y_net, "χ_tilde": χ_tilde, "k_hat": k_hat, "a_total": a_total} +``` + Let $R = \delta_k + \gamma_1$. For a given Pareto-weight vector $\omega$ (pre: $\omega=\mu$; post: $\omega=\lambda^*$), define household assets $$ a_{j,t} \equiv \omega_j k_t + \hat{k}_{j,t}. @@ -1567,110 +1899,74 @@ y^{post}_{j,t} = y_{j,t}(\lambda^*). $$ ```{code-cell} ipython3 -mu_values = np.asarray(paths["u_ratio"]).reshape(-1) +μ_values = np.asarray(paths["μ"]).reshape(-1) t0 = burn_in -c_hh = paths["c_j"][:, t0:] -d_hh = paths["d_j"][:, t0:] -a_hh = paths["bonds_j"][:, t0:] -khat_hh = paths["khat_j"][:, t0:] - -chi_hh = paths["chi_tilde"][:, t0:] -c_share = mu_values[:, None] * paths["c"][0, t0:] - -print(f"max|chi_tilde| over panel = {float(np.max(np.abs(chi_hh))):.6g}") -print(f"std(mu*c_agg) = {float(np.std(c_share)):.6g}, std(chi_tilde) = {float(np.std(chi_hh)):.6g}") -resid = c_hh - c_share -var_c = float(np.var(c_hh)) -var_resid = float(np.var(resid)) -print(f"Variance share explained by mu*c_agg (pooled): {1.0 - (var_resid / var_c):.6f}") - -R = float(deltak + gamma1) - -# Aggregate quantities -c_agg = paths["c"][0, t0:] # aggregate consumption -d_agg = paths["d"][0, t0:] # aggregate endowment -k_agg = paths["k"][0, t0:] # aggregate capital +R = float(δ_k + γ_1) -# Pre-redistribution: income and consumption based on original Pareto weights μ_j -# c_jt = μ_j * c_t + χ̃_jt (Gorman sharing rule) -# y_pre = μ_j * d_t + (R-1) * a_lag (mutual fund income), with a_{j,t} = μ_j k_t + k̂_{j,t} -a_lag = np.concatenate([a_hh[:, [0]], a_hh[:, :-1]], axis=1) -k_lag = np.concatenate([k_agg[[0]], k_agg[:-1]], axis=0)[None, :] # (1, T) -khat_lag = np.concatenate([khat_hh[:, [0]], khat_hh[:, :-1]], axis=1) +# Redistribution parameters (same as visualization above) +red_α = 0.8 +red_β = 0.0 -# Consistency check: total assets a_{j,t-1} = μ_j k_{t-1} + k̂_{j,t-1} -a_lag_check = mu_values[:, None] * k_lag + khat_lag -print(f"max|a_lag - (mu*k_lag + khat_lag)|: {float(np.max(np.abs(a_lag - a_lag_check))):.2e}") +idx_sorted = np.argsort(-μ_values) +λ_orig_sorted = μ_values[idx_sorted] -# Post-redistribution: use redistributed Pareto weights λ_j^* -# Sort original weights, redistribute, then map back to original household ordering -idx_sorted = np.argsort(-mu_values) -lambda_orig_sorted = mu_values[idx_sorted] +λ_star_sorted = create_redistributed_weights(λ_orig_sorted, α=red_α, β=red_β) +λ_star = np.empty_like(μ_values, dtype=float) +λ_star[idx_sorted] = λ_star_sorted -# alpha controls redistribution strength beta controls shape -lambda_star_sorted = create_redistributed_weights(lambda_orig_sorted, alpha=red_alpha, beta=red_beta) -lambda_star = np.empty_like(mu_values, dtype=float) -lambda_star[idx_sorted] = lambda_star_sorted +print(f"Weight redistribution:") +print(f" std(μ): {np.std(μ_values):.4f} → std(λ*): {np.std(λ_star):.4f}") +print(f" p90/p10: {np.percentile(μ_values, 90)/np.percentile(μ_values, 10):.2f} → {np.percentile(λ_star, 90)/np.percentile(λ_star, 10):.2f}") - -# Pre/post allocations implied by weights (hold aggregate paths fixed) -ubs_for_alloc = Ub_list if "Ub_list" in globals() else ubs h0i_alloc = np.array([[0.0]]) -pre = allocation_from_weights(paths, econ, ubs_for_alloc, mu_values, gamma1, lam, h0i_alloc) -post = allocation_from_weights(paths, econ, ubs_for_alloc, lambda_star, gamma1, lam, h0i_alloc) +pre = allocation_from_weights(paths, econ, Ub_list, μ_values, γ_1, Λ, h0i_alloc) +post = allocation_from_weights(paths, econ, Ub_list, λ_star, γ_1, Λ, h0i_alloc) c_pre = pre["c"][:, t0:] y_pre = pre["y_net"][:, t0:] - c_post = post["c"][:, t0:] y_post = post["y_net"][:, t0:] -# Balanced transfer (flow): implements the new allocation with aggregate feasibility -T_flow = c_post - c_pre - -# Budget constraint check: savings = income - consumption -# s_t = y_t - c_t should equal change in assets: a_t - a_{t-1}/R (approx) -savings_pre = y_pre - c_pre -savings_post = y_post - c_post - def _pct(panel, ps=(90, 50, 10)): return np.percentile(panel, ps, axis=0) +T_ts = min(500, c_pre.shape[1] - 50) +t = t0 + np.arange(T_ts) -# Cross-sectional percentiles over time (drop the first period + leave a tail unplotted) -head_drop = 1 -tail_drop = 50 -T_avail = c_pre.shape[1] -start = min(max(head_drop, 0), T_avail) -end = max(start, T_avail - tail_drop) +y_pre_pct = _pct(y_pre[:, :T_ts]) +y_post_pct = _pct(y_post[:, :T_ts]) +c_pre_pct = _pct(c_pre[:, :T_ts]) +c_post_pct = _pct(c_post[:, :T_ts]) -T_ts = min(500, end - start) -t = t0 + start + np.arange(T_ts) - -y_pre_pct = _pct(y_pre[:, start:start + T_ts], ps=(90, 50, 10)) -y_post_pct = _pct(y_post[:, start:start + T_ts], ps=(90, 50, 10)) -c_pre_pct = _pct(c_pre[:, start:start + T_ts], ps=(90, 50, 10)) -c_post_pct = _pct(c_post[:, start:start + T_ts], ps=(90, 50, 10)) +print(f"\nRedistribution effects:") +print(f" income dispersion: {np.std(y_pre):.3f} → {np.std(y_post):.3f}") +print(f" consumption dispersion: {np.std(c_pre):.3f} → {np.std(c_post):.3f}") fig, ax = plt.subplots(1, 3, figsize=(14, 4), sharey=True) -titles = [ - r"Pre-tax income $y^{pre}$", - rf"Post-tax income $y^{{post}}$", - rf"Consumption $c^{{post}}$" -] -panels = [y_pre_pct, y_post_pct, c_post_pct] - -for idx in range(3): - ax[idx].set_title(titles[idx]) - ax[idx].plot(t, panels[idx][0], label="p90") - ax[idx].plot(t, panels[idx][1], label="p50") - ax[idx].plot(t, panels[idx][2], label="p10") - ax[idx].set_xlabel("t") - -plt.legend() + +ax[0].plot(t, y_pre_pct[0], label="p90", lw=2) +ax[0].plot(t, y_pre_pct[1], label="p50", lw=2) +ax[0].plot(t, y_pre_pct[2], label="p10", lw=2) +ax[0].set_xlabel("time") +ax[0].set_ylabel(r"pre-tax income $y^{pre}$") +ax[0].legend() + +ax[1].plot(t, y_post_pct[0], label="p90", lw=2) +ax[1].plot(t, y_post_pct[1], label="p50", lw=2) +ax[1].plot(t, y_post_pct[2], label="p10", lw=2) +ax[1].set_xlabel("time") +ax[1].set_ylabel(r"post-tax income $y^{post}$") +ax[1].legend() + +ax[2].plot(t, c_post_pct[0], label="p90", lw=2) +ax[2].plot(t, c_post_pct[1], label="p50", lw=2) +ax[2].plot(t, c_post_pct[2], label="p10", lw=2) +ax[2].set_xlabel("time") +ax[2].set_ylabel(r"consumption $c^{post}$") +ax[2].legend() plt.tight_layout() plt.show() -``` \ No newline at end of file +``` From 085ed96ffd390d4aac0043c8490f598e15e05d81 Mon Sep 17 00:00:00 2001 From: Humphrey Yang Date: Wed, 24 Dec 2025 23:56:35 +1100 Subject: [PATCH 03/17] updates --- lectures/gorman_heterogeneous_households.md | 409 +++++++++++--------- 1 file changed, 236 insertions(+), 173 deletions(-) diff --git a/lectures/gorman_heterogeneous_households.md b/lectures/gorman_heterogeneous_households.md index c7c286b5..5d7d7607 100644 --- a/lectures/gorman_heterogeneous_households.md +++ b/lectures/gorman_heterogeneous_households.md @@ -25,19 +25,13 @@ kernelspec: # Gorman Heterogeneous Households and Limited Markets -This lecture implements the Gorman heterogeneous-household economy in Chapter 12 of {cite:t}`HS2013` using the `quantecon.DLE` class. +This lecture implements the Gorman heterogeneous-household economy in Chapter 12 of {cite:t}`HansenSargent2013` using the `quantecon.DLE` class. -It complements [](hs_recursive_models), [](growth_in_dles), and [](irfs_in_hall_model) by focusing on how to recover household allocations and portfolios from an aggregate DLE solution. +It complements {doc}`hs_recursive_models`, {doc}`growth_in_dles`, and {doc}`irfs_in_hall_model` by focusing on how to recover household allocations and portfolios from an aggregate DLE solution. The headline result is that a complete-markets allocation can be implemented with a mutual fund and a one-period bond when Gorman aggregation holds. -This lecture does three things. - -- It solves the aggregate planner problem with `DLE`. -- It computes household weights and deviation terms. -- It simulates a limited-markets portfolio strategy that replicates the Arrow–Debreu allocation. - -In addition to what's in Anaconda, this lecture uses the quantecon library. +In addition to what's in Anaconda, this lecture uses the `quantecon` library. ```{code-cell} ipython3 :tags: [hide-output] @@ -63,6 +57,18 @@ The key insight is that Gorman conditions ensure all consumers have parallel Eng This eliminates the standard problem where the utility possibility frontier shifts with endowment changes, making it impossible to rank allocations without specifying distributional weights. +```{note} +Computing a competitive equilibrium typically entails solving a fixed point problem. + +{cite:t}`negishi1960welfare` described a social welfare function that is maximized, subject to resource and technological constraints, by a competitive equilibrium allocation. + +For Negishi, that social welfare function is a "linear combination of the individual utility functions of consumers, with the weights in the combination in inverse proportion to the marginal utilities of income." + +Because Negishi's weights depend on the allocation through the marginal utilities of income, computing a competitive equilibrium via constrained maximization of a Negishi-style welfare function requires finding a fixed point in the weights. + +When they apply, the beauty of Gorman's aggregation conditions is that time series aggregates and market prices can be computed *without* resorting to Negishi's fixed point approach. +``` + With the help of this powerful result, we proceed in three steps in this lecture: 1. Solve the planner's problem and compute selection matrices that map the aggregate state into allocations and prices. @@ -71,10 +77,36 @@ With the help of this powerful result, we proceed in three steps in this lecture We then simulate examples with two and many households. -### Gorman aggregation (static) +### Gorman aggregation To see where the sharing rule comes from, start with a static economy with $n$ goods, price vector $p$, and consumers $j = 1, \ldots, J$. +Let $c^a$ denote the aggregate amount of consumption to be allocated among consumers. + +Associated with $c^a$ is an Edgeworth box and a set of Pareto optimal allocations. + +From the Pareto optimal allocations, one can construct utility allocation surfaces that describe the frontier of alternative feasible utility assignments to individual consumers. + +Imagine moving from the aggregate vector $c^a$ to some other vector $\tilde{c}^a$ and hence to a new Edgeworth box. + +If neither the original box nor the new box contains the other, then it is possible that the utility allocation surfaces for the two boxes may *cross*, in which case there exists no ordering of aggregate consumption that is independent of the utility weights assigned to individual consumers. + +Here is an example showing when aggregation fails. + +Consider a two-person, two-good, pure-exchange economy where agent A has utility function $U^A = X_A^{1/3} Y_A^{2/3}$, while consumer B has utility function $U^B = X_B^{2/3} Y_B^{1/3}$. + +With aggregate endowment pairs $E = (8,3)$ and $E = (3,8)$, the utility possibility frontiers cross, indicating that these two aggregate endowment vectors cannot be ranked in a way that ignores how utility is distributed between consumers A and B. + +Furthermore, for a given endowment, the slope of the consumers' indifference curves at the tangencies that determine the contract curve varies as one moves along the contract curve. + +This means that for a given aggregate endowment, the competitive equilibrium price depends on the allocation between consumers A and B. + +It follows that for this economy, one cannot determine equilibrium prices independently of the equilibrium allocation. + +{cite:t}`gorman1953community` described restrictions on preferences under which it *is* possible to obtain a community preference ordering. + +Whenever Gorman's conditions are satisfied, there occur substantial simplifications in solving multiple-consumer optimal resource allocation problems: in intertemporal contexts, it becomes possible first to determine the optimal allocation of aggregate resources over time, and then allocate aggregate consumption among consumers by assigning utility levels to each person. + Gorman's aggregation conditions amount to assuming each consumer's compensated demand can be written as an affine function of a *scalar* utility index: $$ @@ -82,6 +114,7 @@ c^j = \psi_j(p) + u^j \psi_c(p), $$ where $\psi_c(p)$ is common across consumers and $\psi_j(p)$ is consumer-specific. + Aggregating over consumers gives a representative-consumer demand system $$ @@ -92,25 +125,65 @@ u^a = \sum_{j=1}^J u^j, \psi_a(p) = \sum_{j=1}^J \psi_j(p). $$ -Because the functions involved are homogeneous of degree zero in $p$ (only *relative* prices matter), the implied gradient $p$ can be recovered from the aggregate bundle without knowing how utility is distributed across consumers. +The baseline functions $\psi_j$ and the common function $\psi_c$ are the derivatives of concave functions that are positively homogeneous of degree 1. + +Hence these functions are homogeneous of degree zero in prices, assuring that the slopes of indifference curves should depend only on the *ratio* of prices. + This is why aggregate allocations and prices can be computed *before* household allocations. -In the quadratic specifications used in this lecture (and in the book), the baseline components are degenerate in the sense that $\psi_j(p) = \chi^j$ is independent of $p$. +Mapping $c^a = \psi_a(p) + u^a \psi_c(p)$ can be inverted to obtain a *gradient* vector $p$ that is independent of how utilities are allocated across consumers. + +Since $\psi_c$ and $\psi_a$ are homogeneous of degree zero, gradients are determined only up to a scalar multiple. + +Armed with $p$, we can then allocate utility among $J$ consumers while respecting the adding up constraint. + +The allocation of aggregate consumption across goods and the associated gradient are determined independently of how aggregate utility is divided among consumers. + +A decentralized version of this analysis proceeds as follows. + +Let $W^j$ denote the wealth of consumer $j$ and $W^a$ denote aggregate wealth. +Then $W^j$ should satisfy + +$$ +W^j = p \cdot c^j = p \cdot \psi_j(p) + u^j p \cdot \psi_c(p). +$$ + +Solving for $u^j$ gives + +$$ +u^j = \frac{W^j - p \cdot \psi_j(p)}{p \cdot \psi_c(p)}. +$$ + +Hence, the Engel curve for consumer $j$ is + +$$ +c^j = \psi_j(p) - \frac{p \cdot \psi_j(p)}{p \cdot \psi_c(p)} \psi_c(p) + W^j \frac{\psi_c(p)}{p \cdot \psi_c(p)}. +$$ + +Notice that the coefficient on $W^j$ is the same for all $j$ since $\psi_c(p)/(p \cdot \psi_c(p))$ is a function only of the price vector $p$. + +The individual allocations can be determined from the Engel curves by substituting for $p$ the gradient vector obtained from the representative consumer's optimal allocation problem. + +In the quadratic specifications used in this lecture (and in {cite}`HansenSargent2013`), the baseline components are degenerate in the sense that $\psi_j(p) = \chi^j$ is independent of $p$, where $\chi^j$ is a consumer-specific bliss point represented by a vector with the same dimension as $c^j$. + In that case, the static sharing rule reduces to $$ c^j - \chi^j = \frac{u^j}{u^a}\,(c^a - \chi^a), $$ -which is exactly the form we use below, except that goods are indexed by both dates and states. +so that there is a common scale factor $(u^j/u^a)$ across all goods for person $j$. +Hence the fraction of total utility assigned to consumer $j$ determines his fraction of the vector $(c^a - \chi^a)$. + +This is exactly the form we use below, except that goods are indexed by both dates and states. -### Notation +## Set up Time is discrete, $t = 0,1,2,\dots$. Households are indexed by $j$ and we use $N$ for the number of simulated households. -#### Exogenous state +### Exogenous state The exogenous state $z_t$ follows a first-order vector autoregression @@ -124,18 +197,18 @@ The vector $z_t$ typically contains three types of components. 1. Constant: The first element is set to 1 and remains constant. -2. Aggregate shocks: Components with persistent dynamics (nonzero entries in $A_{22}$) that affect all households. +2. Aggregate shocks: Components with persistent dynamics that affect all households. - In the examples in {ref}`gorman_twohh`, an AR(2) process drives aggregate endowment fluctuations. -3. Idiosyncratic shocks: Components with transitory or zero persistence that enter individual endowments with loadings summing to zero across households. +3. Idiosyncratic shocks: Components that enter individual endowments with loadings summing to zero across households. - These generate cross-sectional heterogeneity while preserving aggregate resource constraints. The selection matrices $U_b$ and $U_d$ pick out which components of $z_t$ affect household preferences (bliss points) and endowments. -#### Aggregate planner state +### Aggregate planner state The aggregate planner state stacks lagged endogenous stocks and current exogenous variables: @@ -145,15 +218,13 @@ $$ Here $h_{t-1}$ is the lagged household durable stock (habits or durables affecting utility), $k_{t-1}$ is lagged physical capital, and $z_t$ is the current exogenous state. -Together, $x_t$ contains everything the planner needs to make decisions at time $t$. - -#### Aggregates +Together, $x_t$ contains everything the planner needs to make decisions at time $t$. Aggregates are economy-wide totals summed across households: consumption $c_t$, investment $i_t$, capital $k_t$, household stock $h_t$, service flow $s_t$, intermediate good $g_t$, bliss $b_t$, and endowment $d_t$. -#### Gorman weight +### Gorman weight -Following the book's notation, let $u^j$ denote the scalar index in the Gorman demand representation above and define +Let $u^j$ denote the scalar index in the Gorman demand representation above and define $$ \mu_j := \frac{u^j}{u^a}, @@ -161,11 +232,9 @@ $$ u^a := \sum_{i=1}^J u^i. $$ -In the quadratic setting used here, we can choose $u^j$ proportional to household $j$'s *time-zero marginal utility of wealth* (the Lagrange multiplier on its intertemporal budget constraint, denoted $\mu_{0j}^w$ in {cite:t}`HS2013`), and then normalize so that the $\mu_j$ sum to one. - -The weights sum to one: $\sum_j \mu_j = 1$. +In the quadratic setting used here, we can choose $u^j$ proportional to household $j$'s *time-zero marginal utility of wealth* (the Lagrange multiplier on its intertemporal budget constraint, denoted $\mu_{0j}^w$ in {cite:t}`HansenSargent2013`), and then normalize so that the $\mu_j$ sum to one. -The sharing rule can be written either in the "baseline" form used in the book, +The sharing rule can be written either in the "baseline" form, $$ c_{jt} - \chi_{jt} = \mu_j (c_t - \chi_t), @@ -177,13 +246,18 @@ $$ c_{jt} = \mu_j c_t + \tilde{\chi}_{jt}. $$ +where $\tilde{\chi}_{jt} = \chi_{jt} - \mu_j \chi_t$ represents household-specific deviations from the proportional share. + + The proportional term $\mu_j c_t$ is household $j$'s share of aggregate consumption. The deviation term $\tilde{\chi}_{jt}$ captures how household $j$'s consumption differs from its proportional share due to preference heterogeneity (bliss points) and initial conditions, and satisfies $\tilde{\chi}_{jt} = \chi_{jt} - \mu_j \chi_t$. These deviations sum to zero across households: $\sum_j \tilde{\chi}_{jt} = 0$. -#### Technologies +### Technologies + +The economy's resource constraint is $$ \Phi_c c_t + \Phi_g g_t + \Phi_i i_t = \Gamma k_{t-1} + d_t, \quad @@ -199,13 +273,14 @@ b_t = U_b z_t, \quad d_t = U_d z_t. $$ -Selection matrices such as $S_c, S_k, \ldots$ satisfy $c_t = S_c x_t$. +Selection matrices such as $S_{(q)}$ map the aggregate state $x_t$ into aggregate quantities such as $q_t = S_{(q)} x_t$ +for $q = c, i, k, h, s, g, b, d$. Shadow-price mappings $M_c, M_k, \ldots$ are used to value streams and recover equilibrium prices. ### The individual household problem -Consider an economy with $J$ consumers indexed by $j = 1, 2, \ldots, J$. +Recall that we operate in an economy with $J$ consumers indexed by $j = 1, 2, \ldots, J$. Consumers differ in preferences and endowments but share a common information set. @@ -235,19 +310,29 @@ where $b_{jt} = U_b^j z_t$ is household $j$'s preference shock, $d_{jt} = U_d^j Prices $(p_{0t}, w_{t0}, \alpha_{0t}, v_0)$ are determined in equilibrium. -Three key features structure the example in this lecture: +This specification confines heterogeneity among consumers to four sources: + +1. Differences in the preference processes $\{b_{jt}\}$, represented by different selections of $U_b^j$. -1. All households share the same technology matrices $(\Lambda, \Pi_h, \Delta_h, \Theta_h)$. +2. Differences in the endowment processes $\{d_{jt}\}$, represented by different selections of $U_d^j$. -2. Heterogeneity enters only through household-specific preference and endowment parameters $(U_b^j, U_d^j, h_{j,-1}, k_{j,-1})$. +3. Differences in initial household capital $h_{j,-1}$. -3. All households observe the same aggregate information $\mathcal{J}_t = [w_t, x_0]$. +4. Differences in initial physical capital $k_{j,-1}$. + +The matrices $\Lambda, \Pi_h, \Delta_h, \Theta_h$ do *not* depend on $j$. + +Because the technology matrices are common across households, every household's demand system has the same functional form. The four sources of heterogeneity listed above determine each household's effective wealth, which in turn determines the Lagrange multiplier $\mu_j$ on the budget constraint. + +This multiplier appears as the household's share of aggregate consumption in the sharing rule {eq}`eq:sharing_rule`. + +All households observe the same aggregate information $\mathcal{J}_t = [w_t, x_0]$. These restrictions enable Gorman aggregation by ensuring that household demands are affine in wealth. -### From individual problems to the aggregate problem +#### From individual problems to the aggregate problem -The Gorman aggregation conditions establish a powerful equivalence: the competitive equilibrium allocation solves a social planner's problem with a specific set of Pareto weights. +As we hinted before, Gorman aggregation conditions establish a powerful equivalence: the competitive equilibrium allocation solves a social planner's problem with a specific set of Pareto weights. We can solve for aggregate quantities by maximizing a weighted sum of household utilities rather than solving each household's problem separately. @@ -319,38 +404,6 @@ The first-order conditions give shadow prices $(M^c_t, M^k_t, M^h_t, M^s_t)$ ass These shadow prices correspond to competitive equilibrium prices. -## Helper routines - -We repeatedly use matrix sums of the form $\sum_{t \ge 0} A_1^t B_1 B_2^\top (A_2^\top)^t$. - -The following helper computes this sum with a doubling algorithm. - -```{code-cell} ipython3 -def doublej2(A1, B1, A2, B2, tol=1e-15, max_iter=10_000): - r""" - Compute V = Σ_{t=0}^∞ A1^t B1 B2' (A2')^t via a doubling algorithm. - """ - A1 = np.asarray(A1, dtype=float) - A2 = np.asarray(A2, dtype=float) - B1 = np.asarray(B1, dtype=float) - B2 = np.asarray(B2, dtype=float) - - α1, α2 = A1.copy(), A2.copy() - V = B1 @ B2.T - diff, it = np.inf, 0 - - while diff > tol and it < max_iter: - α1_next = α1 @ α1 - α2_next = α2 @ α2 - V_next = V + α1 @ V @ α2.T - - diff = np.max(np.abs(V_next - V)) - α1, α2, V = α1_next, α2_next, V_next - it += 1 - - return V -``` - ## Household allocations Under the Gorman sharing rule, household $j$'s consumption is @@ -363,11 +416,7 @@ The proportional term $\mu_j c_t$ is household $j$'s share of aggregate consumpt The deviation term $\tilde{\chi}_{jt}$ captures deviations driven by preference and endowment heterogeneity. -### Algorithm for computing $\mu_j$ and $\tilde{\chi}_{jt}$ - -Computing household allocations requires three steps. - -**Step 1: Solve the household deviation problem.** +In the first step, we solve household $j$'s deviation problem to obtain $\tilde{\chi}_{jt}$ as a function of its deviation state $\tilde{\eta}_{jt} = h_{j,t-1} - \mu_j h_{t-1}$. The "deviation" consumption $\tilde{\chi}_{jt}$ satisfies the inverse canonical representation @@ -386,9 +435,7 @@ When preferences include durables or habits ($\Lambda \neq 0$), the deviation co The code solves this as a linear-quadratic control problem using a scaling trick: multiplying the transition matrices by $\sqrt{\beta}$ converts the discounted problem into an undiscounted one that can be solved with a standard discrete algebraic Riccati equation. -**Step 2: Compute present values.** - -The Gorman weight $\mu_j$ is determined by the household's budget constraint. +Step two is to compute the Gorman weight $\mu_j$ using household $j$'s budget constraint. We compute four present values: @@ -397,9 +444,7 @@ We compute four present values: - $W_{c1}$: present value of the "unit" consumption stream (what it costs to consume $c_t$) - $W_{c2}$: present value of the deviation consumption stream $\{\tilde{\chi}_{jt}\}$ -Each present value is computed using `doublej2` to sum infinite series of the form $\sum_{t \ge 0} \beta^t M_t S_t'$, where $M_t$ is a shadow price and $S_t$ is a selection matrix. - -**Step 3: Solve for the Gorman weight.** +Each present value is computed using `doublej2` below to sum infinite series of the form $\sum_{t \ge 0} \beta^t M_t S_t'$, where $M_t$ is a shadow price and $S_t$ is a selection matrix. The budget constraint pins down $\mu_j$: @@ -411,8 +456,6 @@ The numerator is household $j$'s wealth (initial capital plus present value of e The denominator is the net cost of consuming one unit of aggregate consumption (consumption value minus intermediate good value). -**Step 4: Build selection matrices.** - Finally, the code constructs selection matrices $S_{ci}, S_{hi}, S_{si}$ that map the augmented state $X_t = [h_{j,t-1}^\top, x_t^\top]^\top$ into household $j$'s allocations: $$ @@ -422,6 +465,30 @@ $$ The augmented state includes household $j$'s own lagged durable stock $h_{j,t-1}$ because the deviation term $\tilde{\chi}_{jt}$ depends on it through the inverse canonical representation. ```{code-cell} ipython3 +def doublej2(A1, B1, A2, B2, tol=1e-15, max_iter=10_000): + r""" + Compute V = Σ_{t=0}^∞ A1^t B1 B2' (A2')^t via a doubling algorithm. + """ + A1 = np.asarray(A1, dtype=float) + A2 = np.asarray(A2, dtype=float) + B1 = np.asarray(B1, dtype=float) + B2 = np.asarray(B2, dtype=float) + + α1, α2 = A1.copy(), A2.copy() + V = B1 @ B2.T + diff, it = np.inf, 0 + + while diff > tol and it < max_iter: + α1_next = α1 @ α1 + α2_next = α2 @ α2 + V_next = V + α1 @ V @ α2.T + + diff = np.max(np.abs(V_next - V)) + α1, α2, V = α1_next, α2_next, V_next + it += 1 + + return V + def heter( Λ, Θ_h, Δ_h, Π_h, β, Θ_k, Δ_k, A_22, C_2, @@ -446,7 +513,9 @@ def heter( β = float(np.asarray(β).squeeze()) - # Household deviation problem (Chapter 3 scaling trick) + ## Household deviation problem + + # scaling trick in Chapter 3 of Hansen and Sargent (2013) A = np.asarray(Δ_h, dtype=float) * np.sqrt(β) B = np.asarray(Θ_h, dtype=float) * np.sqrt(β) @@ -481,7 +550,7 @@ def heter( Q_opt = np.linalg.inv(Q_hh + B.T @ V11 @ B) U_b_s = Q_opt @ B.T @ (V12 @ A_22 + V11 @ A12) + Q_inv @ Π_h.T @ U_bi - # Undo √β scaling + # Undo sqrt β scaling A0_dev = A0_dev / np.sqrt(β) # Long-run covariance under aggregate law of motion @@ -512,7 +581,8 @@ def heter( W_c1 = doublej2(β * A_s.T, M_cs.T, A_s.T, S_cs.T) V_s = doublej2(β * A_s, C_s, A_s, C_s) * β / (1 - β) - W_c1 = float((x0s.T @ W_c1 @ x0s + np.trace(M_cs @ V_s @ S_cs.T)).squeeze()) + W_c1 = float( + (x0s.T @ W_c1 @ x0s + np.trace(M_cs @ V_s @ S_cs.T)).squeeze()) S_c2 = np.hstack((np.zeros((M_c.shape[0], n_h + n_k)), U_b_s)) A_s2 = np.block([[A0_dev, Θ_h @ S_c2], [np.zeros((n_x, n_h)), A0]]) @@ -521,13 +591,15 @@ def heter( W_c2 = doublej2(β * A_s2.T, M_cs.T, A_s2.T, S_cs2.T) V_s2 = doublej2(β * A_s2, C_s, A_s2, C_s) * β / (1 - β) - W_c2 = float((x0s2.T @ W_c2 @ x0s2 + np.trace(M_cs @ V_s2 @ S_cs2.T)).squeeze()) + W_c2 = float( + (x0s2.T @ W_c2 @ x0s2 + np.trace(M_cs @ V_s2 @ S_cs2.T)).squeeze()) # Present value of initial capital W_k = float((k0i.T @ ( np.asarray(Δ_k).T @ M_k + np.asarray(Γ).T @ M_d) @ x0).squeeze()) + ## Compute the Gorman weight μ = float(((W_k + W_d - W_c2) / (W_c1 - W_g)).squeeze()) # Household selection matrices on augmented state X_t = [h^i_{t-1}, x_t] @@ -585,52 +657,39 @@ def heter( } ``` -The book sets the intermediate-good multiplier to $M_g = S_g$, so we pass `econ.Sg` for `M_g`. - -## Risk sharing implications - -The Gorman sharing rule has strong implications for risk sharing across households. - -Because the weight $\mu_j$ is time-invariant and determined at date zero, the deviation process $\{c_{jt} - \tilde{\chi}_{jt}\}$ exhibits perfect risk pooling: all households share aggregate consumption risk in fixed proportions. - -Non-separabilities in preferences (over time or across goods) affect only the baseline process $\{\tilde{\chi}_{jt}\}$ and the calculation of the risk-sharing coefficient $\mu_j$. They do not break the proportional sharing of aggregate risk. - -In the special case where the preference shock processes $\{b_{jt}\}$ are deterministic (known at time zero), individual consumption is perfectly correlated with aggregate consumption conditional on initial information. - -The figures we will be showing below confirm this: household consumption paths track aggregate consumption closely. - ## Limited markets This section implements a key result: the Arrow-Debreu allocation can be replicated using only a mutual fund and a one-period bond, rather than a complete set of contingent claims. ### The trading mechanism -Consider an economy where each household $j$ initially owns claims to its own endowment stream $\{d_{jt}\}$. Instead of trading in a complete set of Arrow-Debreu markets, we open: +Consider an economy where each household $j$ initially owns claims to its own endowment stream $\{d_{jt}\}$. + +Instead of trading in a complete set of Arrow-Debreu markets, we open: 1. A stock market with securities paying dividends $\{d_{jt}\}$ for each endowment process 2. A market for one-period riskless bonds At time zero, household $j$ executes the following trades: -1. **Sell individual claims**: Household $j$ sells all shares of its own endowment security -2. **Buy the mutual fund**: Purchase $\mu_j$ shares of all securities (equivalently, $\mu_j$ shares of a mutual fund holding the aggregate endowment) -3. **Adjust bond position**: Take position $\hat{k}_{j0}$ in the one-period bond +1. Household $j$ sells all shares of its own endowment security. +2. Household $j$ purchases $\mu_j$ shares of all securities (equivalently, $\mu_j$ shares of a mutual fund holding the aggregate endowment). +3. Household $j$ takes position $\hat{k}_{j0}$ in the one-period bond. After this initial rebalancing, household $j$ holds the portfolio forever. -### Why $\mu_j$ shares? -The portfolio weight $\mu_j$ is not arbitrary — it is the unique weight that allows the limited-markets portfolio to replicate the Arrow-Debreu consumption allocation. +The portfolio weight $\mu_j$ is not arbitrary: it is the unique weight that allows the limited-markets portfolio to replicate the Arrow-Debreu consumption allocation. -**The requirement.** Under Gorman aggregation, household $j$'s equilibrium consumption satisfies +Under Gorman aggregation, household $j$'s equilibrium consumption satisfies $$ c_{jt} = \mu_j c_t + \tilde{\chi}_{jt}. $$ -We need a portfolio strategy that delivers exactly this consumption stream. +We need a portfolio strategy that delivers exactly this consumption stream. The mutual fund holds claims to all individual endowment streams. -**The mutual fund.** The mutual fund holds claims to all individual endowment streams. Total dividends paid by the fund each period are +Total dividends paid by the fund each period are $$ \sum_{i=1}^J d_{it} = d_t, @@ -638,25 +697,25 @@ $$ the aggregate endowment. If household $j$ holds fraction $\theta_j$ of this fund, it receives $\theta_j d_t$ in dividends. -**Matching the proportional term.** The proportional part of consumption $\mu_j c_t$ must be financed by the mutual fund and capital holdings. Since the aggregate resource constraint is +The proportional part of consumption $\mu_j c_t$ must be financed by the mutual fund and capital holdings. Since the aggregate resource constraint is $$ c_t + i_t = (\delta_k + \gamma_1) k_{t-1} + d_t, $$ -holding $\theta_j$ shares of aggregate output (capital income plus endowments) delivers $\theta_j [(\delta_k + \gamma_1)k_{t-1} + d_t]$. For this to finance $\mu_j c_t$ plus reinvestment $\mu_j k_t$, we need $\theta_j = \mu_j$. +holding $\theta_j$ shares of aggregate output (capital income plus endowments) delivers $\theta_j [(\delta_k + \gamma_1)k_{t-1} + d_t]$. -**The deviation term.** The remaining term $\tilde{\chi}_{jt}$ is financed by adjusting the bond position according to the recursion derived below. +For this to finance $\mu_j c_t$ plus reinvestment $\mu_j k_t$, we need $\theta_j = \mu_j$. -**Conclusion.** Setting $\theta_j = \mu_j$ ensures that the mutual fund finances exactly the proportional share $\mu_j c_t$, while the bond handles the deviation $\tilde{\chi}_{jt}$. This transforms heterogeneous endowment risk into proportional shares of aggregate risk. +The remaining term $\tilde{\chi}_{jt}$ is financed by adjusting the bond position according to the recursion derived below. -### Derivation of the bond recursion +Setting $\theta_j = \mu_j$ thus ensures that the mutual fund finances exactly the proportional share $\mu_j c_t$, while the bond handles the deviation $\tilde{\chi}_{jt}$. -We now derive the law of motion for the bond position $\hat{k}_{jt}$. +This transforms heterogeneous endowment risk into proportional shares of aggregate risk. -**Step 1: Write the budget constraint.** +We now derive the law of motion for the bond position $\hat{k}_{jt}$. -Household $j$'s time-$t$ resources equal uses: +First, write the budget constraint. Household $j$'s time-$t$ resources equal uses: $$ \underbrace{\mu_j [(\delta_k + \gamma_1) k_{t-1} + d_t]}_{\text{mutual fund income}} + \underbrace{R \hat{k}_{j,t-1}}_{\text{bond return}} = \underbrace{c_{jt}}_{\text{consumption}} + \underbrace{\mu_j k_t}_{\text{new fund shares}} + \underbrace{\hat{k}_{jt}}_{\text{new bonds}} @@ -664,16 +723,12 @@ $$ where $R := \delta_k + \gamma_1$ is the gross return. -**Step 2: Substitute the sharing rule.** - -Replace $c_{jt}$ with $\mu_j c_t + \tilde{\chi}_{jt}$: +Substituting the sharing rule by replacing $c_{jt}$ with $\mu_j c_t + \tilde{\chi}_{jt}$ gives: $$ \mu_j [(\delta_k + \gamma_1) k_{t-1} + d_t] + R \hat{k}_{j,t-1} = \mu_j c_t + \tilde{\chi}_{jt} + \mu_j k_t + \hat{k}_{jt} $$ -**Step 3: Use the aggregate resource constraint.** - The aggregate economy satisfies $c_t + k_t = (\delta_k + \gamma_1) k_{t-1} + d_t$, so: $$ @@ -686,8 +741,6 @@ $$ \mu_j (c_t + k_t) + R \hat{k}_{j,t-1} = \mu_j c_t + \tilde{\chi}_{jt} + \mu_j k_t + \hat{k}_{jt} $$ -**Step 4: Simplify.** - The $\mu_j c_t$ and $\mu_j k_t$ terms cancel: $$ @@ -706,9 +759,7 @@ This says that the bond position grows at rate $R$ but is drawn down by the devi To find $\hat{k}_{j0}$, we solve the recursion {eq}`eq:bond-recursion` forward. -**Step 1: Iterate forward.** - -From $\hat{k}_{jt} = R \hat{k}_{j,t-1} - \tilde{\chi}_{jt}$, we get: +Iterating forward from $\hat{k}_{jt} = R \hat{k}_{j,t-1} - \tilde{\chi}_{jt}$, we get: $$ \begin{aligned} @@ -719,9 +770,7 @@ $$ \end{aligned} $$ -**Step 2: Apply transversality.** - -For the budget constraint to hold with equality, we need $\lim_{T \to \infty} R^{-T} \hat{k}_{jT} = 0$. Dividing by $R^T$: +For the budget constraint to hold with equality, we apply transversality and require $\lim_{T \to \infty} R^{-T} \hat{k}_{jT} = 0$. Dividing by $R^T$: $$ R^{-T} \hat{k}_{jT} = \hat{k}_{j0} - \sum_{t=1}^T R^{-t} \tilde{\chi}_{jt} @@ -733,7 +782,7 @@ $$ 0 = \hat{k}_{j0} - \sum_{t=1}^\infty R^{-t} \tilde{\chi}_{jt} $$ -**Step 3: Solve for initial position.** +Solving for the initial position gives: $$ \hat{k}_{j0} = \sum_{t=1}^\infty R^{-t} \tilde{\chi}_{jt}. @@ -743,12 +792,30 @@ This is the present value of future deviation consumption. Since $\tilde{\chi}_{ The household's total asset position is $a_{jt} = \mu_j k_t + \hat{k}_{jt}$. +### Connection to Rubinstein's two-fund theorem + +This construction displays a multiperiod counterpart to an aggregation result for security markets derived by {cite:t}`rubinstein1974aggregation`. + +In a two-period model, Rubinstein provided sufficient conditions on preferences of consumers and asset market payoffs for the implementation of an Arrow-Debreu contingent claims allocation with incomplete security markets. + +In Rubinstein's implementation, all consumers hold the same portfolio of risky assets. + +In our construction, consumers also hold the same portfolio of risky assets, and portfolio weights do not vary over time. + +All changes over time in portfolio composition take place through transactions in the bond market. + +We have allowed for non-separabilities over time in the induced preference ordering for consumption goods. + +These have important effects on bond market transactions. + ```{code-cell} ipython3 def compute_household_paths(econ, U_b_list, U_d_list, x0, x_path, γ_1, Λ, h0i=None, k0i=None): """ Compute household allocations and limited-markets portfolios along a fixed aggregate path. """ + + # Organize inputs x0 = np.asarray(x0, dtype=float).reshape(-1, 1) x_path = np.asarray(x_path, dtype=float) @@ -762,6 +829,8 @@ def compute_household_paths(econ, U_b_list, U_d_list, x0, x_path, γ_1, Λ, h0i= z_path = x_path[n_h + n_k:, :] + + # Select consumption, capital, bonds, and dividends c = econ.Sc @ x_path k = econ.Sk @ x_path b = econ.Sb @ x_path @@ -779,9 +848,10 @@ def compute_household_paths(econ, U_b_list, U_d_list, x0, x_path, γ_1, Λ, h0i= if k0i is None: k0i = np.zeros((n_k, 1)) + # Prepare data containers N = len(U_b_list) _, T = c.shape - + μ = np.empty(N) χ_tilde = np.zeros((N, T)) c_j = np.zeros((N, T)) @@ -791,6 +861,7 @@ def compute_household_paths(econ, U_b_list, U_d_list, x0, x_path, γ_1, Λ, h0i= k_hat = np.zeros((N, T)) a_total = np.zeros((N, T)) + # For each household, compute Gorman weight and paths for j in range(N): U_bj = np.asarray(U_b_list[j], dtype=float) U_dj = np.asarray(U_d_list[j], dtype=float) @@ -810,6 +881,7 @@ def compute_household_paths(econ, U_b_list, U_d_list, x0, x_path, γ_1, Λ, h0i= econ.A0, econ.C, econ.Md, + # The book sets the intermediate-good multiplier to $M_g = S_g$ econ.Sg, econ.Mk, econ.gamma, @@ -840,10 +912,13 @@ def compute_household_paths(econ, U_b_list, U_d_list, x0, x_path, γ_1, Λ, h0i= η[:, t] = (A_h @ η[:, t - 1] + B_h @ b_tilde[:, t]).squeeze() c_j[j] = (μ[j] * c[0] + χ_tilde[j]).squeeze() + # Original endowment claim (before trading) d_j[j] = (U_dj @ z_path)[0, :] + # Dividend income under limited markets: μ_j × aggregate endowment d_share[j] = (μ[j] * d[0]).squeeze() + # Capital share in mutual fund k_share[j] = (μ[j] * k[0]).squeeze() @@ -862,7 +937,7 @@ def compute_household_paths(econ, U_b_list, U_d_list, x0, x_path, γ_1, Λ, h0i= "d": d, "c_j": c_j, "d_j": d_j, - "d_share": d_share, # μ_j × d_t: dividend income under limited markets + "d_share": d_share, "k_share": k_share, "k_hat": k_hat, "a_total": a_total, @@ -876,34 +951,6 @@ def compute_household_paths(econ, U_b_list, U_d_list, x0, x_path, γ_1, Λ, h0i= def solve_model(info, tech, pref, U_b_list, U_d_list, γ_1, Λ, z0, ts_length=2000): """ Solve the representative-agent DLE problem and compute household paths. - - Parameters - ---------- - info : tuple - (A_22, C_2, U_b, U_d) - Information structure - tech : tuple - (Φ_c, Φ_g, Φ_i, Γ, δ_k, θ_k) - Technology parameters - pref : tuple - (β, Λ, Π_h, δ_h, θ_h) - Preference parameters - U_b_list : list - List of household-specific bliss matrices - U_d_list : list - List of household-specific endowment matrices - γ_1 : float - Capital productivity parameter - Λ : float or array - Durable service flow parameter - z0 : array - Initial exogenous state (will be augmented to full state) - ts_length : int, optional - Length of simulation (default: 2000) - - Returns - ------- - paths : dict - Dictionary containing household paths - econ : DLE - DLE economy object """ econ = DLE(info, tech, pref) @@ -935,6 +982,22 @@ def solve_model(info, tech, pref, U_b_list, U_d_list, γ_1, Λ, z0, ts_length=20 We reproduce the two-household Hall-style calibration from Chapter 12.6 of {cite:t}`HS2013`. +The discount factor $\beta = 1/(\gamma_1 + \delta_k)$ is chosen so that $\beta(\gamma_1 + \delta_k) = 1$, a steady-state condition that makes the planner indifferent between consuming today and investing for tomorrow. + +Capital depreciates at 5% per period ($\delta_k = 0.95$) and earns a 10% return ($\gamma_1 = 0.1$). + +The adjustment cost $\phi_1 = 10^{-5}$ is negligible, approximating frictionless investment. + +For preferences, setting $\Lambda = 0$ eliminates habit formation from the service flow, so utility depends only on current consumption ($s_t = \Pi_h c_t$ with $\Pi_h = 1$). + +The habit stock still accumulates ($\delta_h = 0.2$, $\theta_h = 0.1$) but does not affect welfare directly. + +The exogenous state $z_t = [1, d_{a,t}, d_{a,t-1}, w_{1t}, w_{2t}]'$ contains a constant, an AR(2) aggregate endowment process with coefficients $(1.2, -0.22)$, and two i.i.d. idiosyncratic shocks. + +Household 1 has mean endowment 4 with exposure 0.2 to idiosyncratic shock $w_{1t}$, while household 2 has mean endowment 3 with unit exposure to the AR(2) process. + +Both households share bliss point 15. + ```{code-cell} ipython3 ϕ_1 = 1e-5 γ_1 = 0.1 @@ -1029,6 +1092,14 @@ ax.legend() plt.show() ``` +The Gorman sharing rule has strong implications for risk sharing across households. + +Because the weight $\mu_j$ is time-invariant and determined at date zero, the deviation process $\{c_{jt} - \tilde{\chi}_{jt}\}$ exhibits perfect risk pooling: all households share aggregate consumption risk in fixed proportions. + +Non-separabilities in preferences (over time or across goods) affect only the baseline process $\{\tilde{\chi}_{jt}\}$ and the calculation of the risk-sharing coefficient $\mu_j$. They do not break the proportional sharing of aggregate risk. + +In this special case where the preference shock processes $\{b_{jt}\}$ are deterministic (known at time zero), individual consumption is perfectly correlated with aggregate consumption conditional on initial information. + The next figure plots the limited-markets bond adjustment and confirms that the adjustments sum to approximately zero. ```{code-cell} ipython3 @@ -1135,13 +1206,8 @@ def build_reverse_engineered_gorman_extended( with rho_idio = 0 recovering i.i.d. shocks (AR(0)). - Preference shocks xi_{j,t} are included for each household. - - Parameters - ---------- - n_absorb : int, optional - Number of households that absorb the idiosyncratic shocks. - If None, defaults to max(1, n // 10) (10% of households, at least 1). + Preference shocks xi_{j,t} are included for each household but + can be set to zero if desired. """ alphas = np.asarray(alphas).reshape(-1) phis = np.asarray(phis).reshape(-1) @@ -1157,7 +1223,8 @@ def build_reverse_engineered_gorman_extended( if n_absorb < 1 or n_absorb >= n: raise ValueError(f"n_absorb must be in [1, n-1], got {n_absorb} with n={n}") - # State vector: z_t = [1, d_{a,t}, d_{a,t-1}, eta_{n_absorb+1,t}, ..., eta_{n,t}, xi_{1,t}, ..., xi_{n,t}] + # State vector: + # z_t = [1, d_{a,t}, d_{a,t-1}, eta_{n_absorb+1,t}, ..., eta_{n,t}, xi_{1,t}, ..., xi_{n,t}] # where eta_{j,t} are idiosyncratic endowment shocks (j=n_absorb+1..n) # and xi_{j,t} are preference shocks (j=1..n) # First n_absorb households absorb -1/n_absorb * sum(eta_j) to ensure shocks sum to zero @@ -1426,9 +1493,7 @@ A0[:2, :2] ### Impulse responses -We compute impulse responses to show how shocks propagate through the economy. - -**Methodology**: The closed-loop state evolves as $x_{t+1} = A_0 x_t + C w_{t+1}$ where $w_{t+1}$ are i.i.d. shocks. +We compute impulse responses to show how shocks propagate through the economy. The closed-loop state evolves as $x_{t+1} = A_0 x_t + C w_{t+1}$ where $w_{t+1}$ are i.i.d. shocks. To trace an impulse response: 1. Set $x_0 = C e_j \times \sigma$ where $e_j$ is the $j$-th standard basis vector and $\sigma$ is the shock size @@ -1505,8 +1570,6 @@ plt.tight_layout() plt.show() ``` -**Interpreting the results**: - The diagnostics above show which state variables are hit by the aggregate shock and the resulting propagation through the economy. If $d_{a,t}$ shows clear AR(2) dynamics, this confirms the aggregate endowment shock propagates correctly through the closed-loop system. From 4d32fc2ce4bc4536023feee61fc42040a168417c Mon Sep 17 00:00:00 2001 From: Humphrey Yang Date: Wed, 24 Dec 2025 23:57:37 +1100 Subject: [PATCH 04/17] update --- lectures/_static/quant-econ.bib | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/lectures/_static/quant-econ.bib b/lectures/_static/quant-econ.bib index 16326795..73304061 100644 --- a/lectures/_static/quant-econ.bib +++ b/lectures/_static/quant-econ.bib @@ -3,6 +3,39 @@ Note: Extended Information (like abstracts, doi, url's etc.) can be found in quant-econ-extendedinfo.bib file in _static/ ### +@article{gorman1953community, + title={Community preference fields}, + author={Gorman, William M.}, + journal={Econometrica: Journal of the Econometric Society}, + volume={21}, + number={1}, + pages={63--80}, + year={1953}, + publisher={JSTOR} +} + +@article{negishi1960welfare, + title={Welfare economics and existence of an equilibrium for a competitive economy}, + author={Negishi, Takashi}, + journal={Metroeconomica}, + volume={12}, + number={2-3}, + pages={92--97}, + year={1960}, + publisher={Wiley Online Library} +} + +@article{rubinstein1974aggregation, + title={An aggregation theorem for securities markets}, + author={Rubinstein, Mark}, + journal={Journal of Financial Economics}, + volume={1}, + number={3}, + pages={225--244}, + year={1974}, + publisher={Elsevier} +} + @techreport{boerma2023composite, title={Composite sorting}, author={Boerma, Job and Tsyvinski, Aleh and Wang, Ruodu and Zhang, Zhenyuan}, From a3214e195eeb8b1f56520258d5473b5ae4f0a055 Mon Sep 17 00:00:00 2001 From: Humphrey Yang Date: Thu, 25 Dec 2025 12:25:58 +1100 Subject: [PATCH 05/17] updates --- lectures/gorman_heterogeneous_households.md | 429 +++++++------------- 1 file changed, 147 insertions(+), 282 deletions(-) diff --git a/lectures/gorman_heterogeneous_households.md b/lectures/gorman_heterogeneous_households.md index 5d7d7607..f392f4bf 100644 --- a/lectures/gorman_heterogeneous_households.md +++ b/lectures/gorman_heterogeneous_households.md @@ -437,16 +437,25 @@ The code solves this as a linear-quadratic control problem using a scaling trick Step two is to compute the Gorman weight $\mu_j$ using household $j$'s budget constraint. -We compute four present values: +We compute five present values: - $W_d$: present value of household $j$'s endowment stream $\{d_{jt}\}$ - $W_k$: value of household $j$'s initial capital $k_{j,-1}$ - $W_{c1}$: present value of the "unit" consumption stream (what it costs to consume $c_t$) - $W_{c2}$: present value of the deviation consumption stream $\{\tilde{\chi}_{jt}\}$ +- $W_g$: present value of the intermediate good stream $\{g_t\}$ -Each present value is computed using `doublej2` below to sum infinite series of the form $\sum_{t \ge 0} \beta^t M_t S_t'$, where $M_t$ is a shadow price and $S_t$ is a selection matrix. +Each present value is computed using `doublej2` below to sum infinite series. -The budget constraint pins down $\mu_j$: +The budget constraint requires that the present value of consumption equals wealth. + +Substituting $c_{jt} = \mu_j c_t + \tilde{\chi}_{jt}$ gives + +$$ +\mu_j W_{c1} + W_{c2} = W_k + W_d + \mu_j W_g, +$$ + +where $\mu_j W_g$ is household $j$'s share of intermediate good value. Solving for $\mu_j$: $$ \mu_j = \frac{W_k + W_d - W_{c2}}{W_{c1} - W_g}. @@ -663,7 +672,7 @@ This section implements a key result: the Arrow-Debreu allocation can be replica ### The trading mechanism -Consider an economy where each household $j$ initially owns claims to its own endowment stream $\{d_{jt}\}$. +Consider again an economy where each household $j$ initially owns claims to its own endowment stream $\{d_{jt}\}$. Instead of trading in a complete set of Arrow-Debreu markets, we open: @@ -687,7 +696,9 @@ $$ c_{jt} = \mu_j c_t + \tilde{\chi}_{jt}. $$ -We need a portfolio strategy that delivers exactly this consumption stream. The mutual fund holds claims to all individual endowment streams. +We need a portfolio strategy that delivers exactly this consumption stream. + +The mutual fund holds claims to all individual endowment streams. Total dividends paid by the fund each period are @@ -695,7 +706,9 @@ $$ \sum_{i=1}^J d_{it} = d_t, $$ -the aggregate endowment. If household $j$ holds fraction $\theta_j$ of this fund, it receives $\theta_j d_t$ in dividends. +which is the aggregate endowment. + +If household $j$ holds fraction $\theta_j$ of this fund, it receives $\theta_j d_t$ in dividends. The proportional part of consumption $\mu_j c_t$ must be financed by the mutual fund and capital holdings. Since the aggregate resource constraint is @@ -753,7 +766,12 @@ $$ \hat{k}_{jt} = R \hat{k}_{j,t-1} - \tilde{\chi}_{jt}. $$ (eq:bond-recursion) -This says that the bond position grows at rate $R$ but is drawn down by the deviation consumption $\tilde{\chi}_{jt}$. When $\tilde{\chi}_{jt} > 0$ (household $j$ consumes more than its share), it finances this by running down its bond holdings. +This says that the bond position grows at rate $R$ but is drawn down by the deviation consumption $\tilde{\chi}_{jt}$. + +When $\tilde{\chi}_{jt} > 0$ (household $j$ consumes more than its share), it finances this by running down its bond holdings. + +The household's total asset position is hence $a_{jt} = \mu_j k_t + \hat{k}_{jt}$. + ### Initial bond position @@ -788,11 +806,11 @@ $$ \hat{k}_{j0} = \sum_{t=1}^\infty R^{-t} \tilde{\chi}_{jt}. $$ -This is the present value of future deviation consumption. Since $\tilde{\chi}_{jt}$ depends only on the deterministic deviation between household $j$'s preference shocks and its share of aggregate preference shocks, this sum is in the time-zero information set and can be computed at date zero. +This is the present value of future deviation consumption. -The household's total asset position is $a_{jt} = \mu_j k_t + \hat{k}_{jt}$. +Since $\tilde{\chi}_{jt}$ depends only on the deterministic deviation between household $j$'s preference shocks and its share of aggregate preference shocks, this sum is in the time-zero information set and can be computed at date zero. -### Connection to Rubinstein's two-fund theorem +```{note} Connection to Rubinstein's two-fund theorem This construction displays a multiperiod counterpart to an aggregation result for security markets derived by {cite:t}`rubinstein1974aggregation`. @@ -803,10 +821,9 @@ In Rubinstein's implementation, all consumers hold the same portfolio of risky a In our construction, consumers also hold the same portfolio of risky assets, and portfolio weights do not vary over time. All changes over time in portfolio composition take place through transactions in the bond market. +``` -We have allowed for non-separabilities over time in the induced preference ordering for consumption goods. - -These have important effects on bond market transactions. +Below is the code that computes household allocations and limited-markets portfolios along a fixed aggregate path according to the mechanism described above ```{code-cell} ipython3 def compute_household_paths(econ, U_b_list, U_d_list, x0, x_path, γ_1, Λ, h0i=None, k0i=None): @@ -922,6 +939,7 @@ def compute_household_paths(econ, U_b_list, U_d_list, x0, x_path, γ_1, Λ, h0i= # Capital share in mutual fund k_share[j] = (μ[j] * k[0]).squeeze() + # Solving initial bond position and path if abs(R - 1.0) >= 1e-14: k_hat[j, -1] = χ_tilde[j, -1] / (R - 1.0) for t in range(T - 1, 0, -1): @@ -947,6 +965,9 @@ def compute_household_paths(econ, U_b_list, U_d_list, x0, x_path, γ_1, Λ, h0i= } ``` +The next function collects everything to solve the planner's problem and compute household paths +into one function + ```{code-cell} ipython3 def solve_model(info, tech, pref, U_b_list, U_d_list, γ_1, Λ, z0, ts_length=2000): """ @@ -980,23 +1001,28 @@ def solve_model(info, tech, pref, U_b_list, U_d_list, γ_1, Λ, z0, ts_length=20 (gorman_twohh)= ## Example: two-household economy -We reproduce the two-household Hall-style calibration from Chapter 12.6 of {cite:t}`HS2013`. - -The discount factor $\beta = 1/(\gamma_1 + \delta_k)$ is chosen so that $\beta(\gamma_1 + \delta_k) = 1$, a steady-state condition that makes the planner indifferent between consuming today and investing for tomorrow. +We reproduce the two-household example from Chapter 12.6 of {cite:t}`HansenSargent2013`. -Capital depreciates at 5% per period ($\delta_k = 0.95$) and earns a 10% return ($\gamma_1 = 0.1$). +There are two households, each with preferences -The adjustment cost $\phi_1 = 10^{-5}$ is negligible, approximating frictionless investment. +$$ +-\frac{1}{2} \mathbb{E} \sum_{t=0}^{\infty} \beta^t \left[(c^i_t - b^i_t)^2 + \ell^{i\,2}_t\right] \mid J_0, \quad i = 1, 2. +$$ -For preferences, setting $\Lambda = 0$ eliminates habit formation from the service flow, so utility depends only on current consumption ($s_t = \Pi_h c_t$ with $\Pi_h = 1$). +We set $b^i_t = 15$ for $i = 1, 2$, so the aggregate preference shock is $b_t = \sum_i b^i_t = 30$. The endowment processes are -The habit stock still accumulates ($\delta_h = 0.2$, $\theta_h = 0.1$) but does not affect welfare directly. +$$ +d^1_t = 4 + 0.2\, w^1_t, \qquad +d^2_t = 3 + \tilde{d}^2_t, +$$ -The exogenous state $z_t = [1, d_{a,t}, d_{a,t-1}, w_{1t}, w_{2t}]'$ contains a constant, an AR(2) aggregate endowment process with coefficients $(1.2, -0.22)$, and two i.i.d. idiosyncratic shocks. +where $w^1_t$ is Gaussian white noise with variance $(0.2)^2$, and $\tilde{d}^2_t$ follows -Household 1 has mean endowment 4 with exposure 0.2 to idiosyncratic shock $w_{1t}$, while household 2 has mean endowment 3 with unit exposure to the AR(2) process. +$$ +\tilde{d}^2_t = 1.2\, \tilde{d}^2_{t-1} - 0.22\, \tilde{d}^2_{t-2} + 0.25\, w^2_t, +$$ -Both households share bliss point 15. +with $w^2_t$ Gaussian white noise with variance $(0.25)^2$. ```{code-cell} ipython3 ϕ_1 = 1e-5 @@ -1005,8 +1031,8 @@ Both households share bliss point 15. β = 1.0 / (γ_1 + δ_k) θ_k = 1.0 -δ_h = 0.2 -θ_h = 0.1 +δ_h = 0.0 +θ_h = 0.0 Λ = 0.0 Π_h = 1.0 @@ -1070,10 +1096,10 @@ paths = compute_household_paths( γ_1=γ_1, Λ=Λ, ) - -paths["μ"] ``` +The next figure plots the consumption paths for the aggregate economy and the two households + ```{code-cell} ipython3 --- mystnb: @@ -1083,9 +1109,9 @@ mystnb: --- T_plot = 250 fig, ax = plt.subplots() -ax.plot(paths["c"][0, :T_plot], lw=2, label="aggregate") -ax.plot(paths["c_j"][0, :T_plot], lw=2, label="household 1") -ax.plot(paths["c_j"][1, :T_plot], lw=2, label="household 2") +ax.plot(paths["c"][0, 5:T_plot], lw=2, label="aggregate") +ax.plot(paths["c_j"][0, 5:T_plot], lw=2, label="household 1") +ax.plot(paths["c_j"][1, 5:T_plot], lw=2, label="household 2") ax.set_xlabel("time") ax.set_ylabel("consumption") ax.legend() @@ -1096,9 +1122,9 @@ The Gorman sharing rule has strong implications for risk sharing across househol Because the weight $\mu_j$ is time-invariant and determined at date zero, the deviation process $\{c_{jt} - \tilde{\chi}_{jt}\}$ exhibits perfect risk pooling: all households share aggregate consumption risk in fixed proportions. -Non-separabilities in preferences (over time or across goods) affect only the baseline process $\{\tilde{\chi}_{jt}\}$ and the calculation of the risk-sharing coefficient $\mu_j$. They do not break the proportional sharing of aggregate risk. +Non-separabilities in preferences (over time or across goods) affect only the baseline process $\{\tilde{\chi}_{jt}\}$ and the calculation of the risk-sharing coefficient $\mu_j$. -In this special case where the preference shock processes $\{b_{jt}\}$ are deterministic (known at time zero), individual consumption is perfectly correlated with aggregate consumption conditional on initial information. +They do not break the proportional sharing of aggregate risk. The next figure plots the limited-markets bond adjustment and confirms that the adjustments sum to approximately zero. @@ -1110,9 +1136,9 @@ mystnb: name: fig-gorman-bond-adjustment --- fig, ax = plt.subplots() -ax.plot(paths["k_hat"][0, :T_plot], lw=2, label="household 1") -ax.plot(paths["k_hat"][1, :T_plot], lw=2, label="household 2") -ax.plot(paths["k_hat"][:, :T_plot].sum(axis=0), lw=2, label="sum") +ax.plot(paths["k_hat"][0, 5:T_plot], lw=2, label="household 1") +ax.plot(paths["k_hat"][1, 5:T_plot], lw=2, label="household 2") +ax.plot(paths["k_hat"][:, 5:T_plot].sum(axis=0), lw=2, label="sum") ax.axhline(0.0, color="k", lw=1, alpha=0.5) ax.set_xlabel("time") ax.set_ylabel("bond position") @@ -1120,13 +1146,15 @@ ax.legend() plt.show() ``` +The final check is to verify that the bond positions sum to zero at all times, confirming that the limited-markets implementation is self-financing + ```{code-cell} ipython3 np.max(np.abs(paths["k_hat"].sum(axis=0))) ``` ## Many-household Gorman economies -This section presents a scalable Gorman economy specification that accommodates many households with heterogeneous endowments and preferences. +We now extend the two-household example to an economy with many households. ### Specification @@ -1146,16 +1174,16 @@ $$ where we can set the $\rho_j$'s to capture persistent aggregate fluctuations. -Individual endowments are: +Let $n_a$ denote the number of "absorbing" households. For households $j > n_a$, individual endowments are $$ -d_{jt} = \alpha_j + \phi_j d_{a,t} + \sigma_j w_{j,t}, \quad j = 2, \ldots, n, +d_{jt} = \alpha_j + \phi_j d_{a,t} + \sigma_j w_{j,t}, \quad j = n_a + 1, \ldots, n. $$ -and for household 1 (which absorbs the negative of all idiosyncratic shocks to ensure aggregation): +The first $n_a$ households absorb the negative of all idiosyncratic shocks to ensure aggregation: $$ -d_{1t} = \alpha_1 + \phi_1 d_{a,t} - \sum_{j=2}^n \sigma_j w_{j,t}. +d_{jt} = \alpha_j + \phi_j d_{a,t} - \frac{1}{n_a} \sum_{k=n_a+1}^n \sigma_k w_{k,t}, \quad j = 1, \ldots, n_a. $$ This construction ensures that @@ -1170,7 +1198,7 @@ $$ \sum_{j=1}^n d_{j,t} = \sum_{j=1}^n \alpha_j + d_{a,t}. $$ -Preference shocks are muted to simplify initial experiments: +Preference shocks are muted to simplify experiments: $$ b_{jt} = \bar{b} + \gamma_j w_{n+j,t}, \quad j = 1, \ldots, n, @@ -1196,7 +1224,7 @@ def build_reverse_engineered_gorman_extended( z_t = [1, d_{a,t}, d_{a,t-1}, eta_{n_absorb+1,t}, ..., eta_{n,t}, xi_{1,t}, ..., xi_{n,t}] The first n_absorb households absorb the negative sum of all idiosyncratic shocks - to ensure shocks sum to zero (Gorman requirement): + to ensure shocks sum to zero: sum_{j=1}^{n_absorb} (-1/n_absorb * sum_{k>n_absorb} eta_k) + sum_{k>n_absorb} eta_k = 0 Each household k > n_absorb has its own idiosyncratic endowment shock eta_{k,t} @@ -1235,18 +1263,20 @@ def build_reverse_engineered_gorman_extended( nz = 3 + n_idio + n_pref nw = 1 + n_idio + n_pref # aggregate + idio + pref shocks - # Persistence parameters (scalars broadcast to vectors) + # Persistence parameters rho_idio = np.asarray(rho_idio, dtype=float) if rho_idio.ndim == 0: rho_idio = np.full(n_idio, float(rho_idio)) if rho_idio.shape != (n_idio,): - raise ValueError(f"rho_idio must be scalar or shape ({n_idio},), got {rho_idio.shape}") + raise ValueError( + f"rho_idio must be scalar or shape ({n_idio},), got {rho_idio.shape}") rho_pref = np.asarray(rho_pref, dtype=float) if rho_pref.ndim == 0: rho_pref = np.full(n_pref, float(rho_pref)) if rho_pref.shape != (n_pref,): - raise ValueError(f"rho_pref must be scalar or shape ({n_pref},), got {rho_pref.shape}") + raise ValueError( + f"rho_pref must be scalar or shape ({n_pref},), got {rho_pref.shape}") # A22: transition matrix A22 = np.zeros((nz, nz)) @@ -1261,16 +1291,18 @@ def build_reverse_engineered_gorman_extended( # C2: shock loading C2 = np.zeros((nz, nw)) - C2[1, 0] = sigma_a # aggregate shock -> d_{a,t} + C2[1, 0] = sigma_a for j in range(n_idio): - # Map to households n_absorb+1, ..., n (indices n_absorb, ..., n-1 in 0-based) + # Map to households n_absorb+1, ..., n C2[3 + j, 1 + j] = sigmas[n_absorb + j] for j in range(n_pref): C2[3 + n_idio + j, 1 + n_idio + j] = gammas[j] # gamma_j -> xi_j # Ud_per_house: endowment loading - # First n_absorb households: d_{jt} = alpha_j + phi_j * d_{a,t} - (1/n_absorb) * sum_{k>n_absorb} eta_{k,t} - # Remaining households k > n_absorb: d_{kt} = alpha_k + phi_k * d_{a,t} + eta_{k,t} + # First n_absorb households: + # d_{jt} = alpha_j + phi_j * d_{a,t} - (1/n_absorb) * sum_{k>n_absorb} eta_{k,t} + # Remaining households k > n_absorb: + # d_{kt} = alpha_k + phi_k * d_{a,t} + eta_{k,t} Ud_per_house = [] for j in range(n): block = np.zeros((2, nz)) @@ -1278,12 +1310,9 @@ def build_reverse_engineered_gorman_extended( block[0, 1] = phis[j] # loading on d_{a,t} if j < n_absorb: - # Absorbing households: load -1/n_absorb on each idiosyncratic shock for k in range(n_idio): block[0, 3 + k] = -1.0 / n_absorb else: - # Non-absorbing household j loads on its own shock eta_j - # Household j (j >= n_absorb) corresponds to eta_{j+1} at position 3 + (j - n_absorb) block[0, 3 + (j - n_absorb)] = 1.0 Ud_per_house.append(block) @@ -1315,15 +1344,15 @@ We now instantiate a 100-household economy using the reverse-engineered Gorman s We use the same technology and preference parameters as the two-household example. ```{code-cell} ipython3 -# Technology and preference parameters (same as two-household example) +# Technology and preference parameters ϕ_1 = 1e-5 γ_1 = 0.1 δ_k = 0.95 β = 1.0 / (γ_1 + δ_k) θ_k = 1.0 -δ_h = 0.2 -θ_h = 0.1 +δ_h = 0.0 +θ_h = 0.0 Λ = 0.0 Π_h = 1.0 @@ -1333,31 +1362,38 @@ We use the same technology and preference parameters as the two-household exampl Γ = np.array([[γ_1], [0.0]]) ``` +We set household-specific parameters below and impose $\sum_j \phi_j = 1$. + ```{code-cell} ipython3 np.random.seed(42) N = 100 +# Aggregate endowment process parameters ρ1 = 0.95 ρ2 = 0.0 σ_a = 0.5 -αs = np.random.uniform(3.0, 5.0, N) +# Mean endowments α_j and aggregate exposure φ_j +αs = np.random.uniform(3.0, 5.0, N) φs_raw = np.random.uniform(0.5, 1.5, N) -φs = φs_raw / np.sum(φs_raw) +φs = φs_raw / np.sum(φs_raw) # normalize so Σ φ_j = 1 +# Rank households by mean endowment to assign idiosyncratic risk wealth_rank_proxy = np.argsort(np.argsort(αs)) wealth_pct_proxy = (wealth_rank_proxy + 0.5) / N poorness = 1.0 - wealth_pct_proxy +# First n_absorb households absorb idiosyncratic shocks n_absorb = 50 +# Poorer households face larger, more persistent idiosyncratic shocks σ_idio_min, σ_idio_max = 0.2, 5.0 σs = σ_idio_min + (σ_idio_max - σ_idio_min) * (poorness ** 2.0) - ρ_idio_min, ρ_idio_max = 0.0, 0.98 ρ_idio = ρ_idio_min + (ρ_idio_max - ρ_idio_min) * (poorness[n_absorb:] ** 1.0) +# Preference shocks (disabled by default) b_bar = 5.0 enable_pref_shocks = False pref_shock_scale = 0.5 @@ -1370,7 +1406,7 @@ else: γs_pref = np.zeros(N) ρ_pref = 0.0 -burn_in = 200 +t0 = 200 A22, C2, Ub, Ud, Ub_list, Ud_list, x0 = build_reverse_engineered_gorman_extended( n=N, @@ -1414,11 +1450,11 @@ T_plot = 50 fig, axes = plt.subplots(1, 2, figsize=(14, 4)) -axes[0].plot(paths["c_j"][:, burn_in:burn_in+T_plot].T, lw=2) +axes[0].plot(paths["c_j"][:, t0:t0+T_plot].T) axes[0].set_xlabel("time") axes[0].set_ylabel("consumption") -axes[1].plot(paths["d_share"][:, burn_in:burn_in+T_plot].T, lw=2) +axes[1].plot(paths["d_share"][:, t0:t0+T_plot].T) axes[1].set_xlabel("time") axes[1].set_ylabel("dividends") @@ -1449,7 +1485,9 @@ $$ with $h_{t-1}$ the household service stock, $k_{t-1}$ the capital stock, and $z_t$ the exogenous state (constant, aggregate endowment states, and idiosyncratic shock states). -Any equilibrium quantity is a linear function of the state. The `quantecon.DLE` module provides selection matrices $S_\bullet$ such that: +Any equilibrium quantity is a linear function of the state. + +The `quantecon.DLE` module provides selection matrices $S_\bullet$ such that: $$ c_t = S_c x_t,\quad g_t = S_g x_t,\quad i_t = S_i x_t,\quad h_t = S_h x_t,\quad k_t = S_k x_t,\quad d_t = S_d x_t,\quad b_t = S_b x_t,\quad s_t = S_s x_t. @@ -1466,8 +1504,6 @@ S_c\\ S_g\\ S_i\\ S_h\\ S_k\\ S_d\\ S_b\\ S_s y_t = G x_t. $$ -The simulated state path is stored in `paths["x_path"]`, where `x_path[:, t]` corresponds to $x_t$. - ```{code-cell} ipython3 A0 = econ.A0 C = econ.C @@ -1487,18 +1523,13 @@ print(f"Shapes: A0 {A0.shape}, C {C.shape}, G {G.shape}") print(f"max |A0[2:,2:] - A22| = {np.max(np.abs(A0[2:, 2:] - A22)):.2e}") ``` -```{code-cell} ipython3 -A0[:2, :2] -``` +With the state space representation, we can compute impulse responses to show how shocks propagate through the economy. -### Impulse responses +To trace the impulse response to shock $j$, we set `shock_idx=j` which selects column $j$ of the loading matrix $C$. -We compute impulse responses to show how shocks propagate through the economy. The closed-loop state evolves as $x_{t+1} = A_0 x_t + C w_{t+1}$ where $w_{t+1}$ are i.i.d. shocks. +This creates an initial state $x_0 = C e_j \sigma$ where $e_j$ is the $j$-th standard basis vector and $\sigma$ is the shock size. -To trace an impulse response: -1. Set $x_0 = C e_j \times \sigma$ where $e_j$ is the $j$-th standard basis vector and $\sigma$ is the shock size -2. Iterate forward with $x_{t+1} = A_0 x_t$ (no further shocks) -3. Compute observables via measurement equation: $y_t = G x_t$ +We then iterate forward with $x_{t+1} = A_0 x_t$ and compute observables via $y_t = G x_t$. ```{code-cell} ipython3 def compute_irf(A0, C, G, shock_idx, T=50, shock_size=1.0): @@ -1523,34 +1554,12 @@ def compute_irf(A0, C, G, shock_idx, T=50, shock_size=1.0): n_h = np.atleast_2d(econ.thetah).shape[0] n_k = np.atleast_2d(econ.thetak).shape[0] -print(f"Diagnostics:") -print(f" State dimension: {A0.shape[0]}") -print(f" n_h={n_h}, n_k={n_k}") -print(f" x_t = [h_{{-1}}, k_{{-1}}, z_t]") -print(f" z_t = [const, d_{{a,t}}, d_{{a,t-1}}, ...]") -print(f"\nShock loading C shape: {C.shape}") -print(f" Shock 0 impact on first 10 states:") -for i in range(min(10, C.shape[0])): - if abs(C[i, 0]) > 1e-10: - print(f" State {i}: {C[i, 0]:.4f}") - idx_da = n_h + n_k + 1 -print(f"\nd_{{a,t}} should be at state index {idx_da}") -print(f" C[{idx_da}, 0] = {C[idx_da, 0]:.6f}") - T_irf = 50 shock_size = 1.0 irf_x, irf_y = compute_irf(A0, C, G, shock_idx=0, T=T_irf, shock_size=shock_size) - -idx_c = 0 -idx_g = 1 -idx_i = 2 idx_k = 4 -print(f"\nImpact of unit shock on state d_{{a,t}}: {irf_x[idx_da, 0]:.6f}") -print(f"Impact on consumption: {irf_y[idx_c, 0]:.6f}") -print(f"Impact on investment: {irf_y[idx_i, 0]:.6f}") -print(f"Impact on capital: {irf_y[idx_k, 0]:.6f}") da_irf = irf_x[idx_da, :] @@ -1570,16 +1579,17 @@ plt.tight_layout() plt.show() ``` -The diagnostics above show which state variables are hit by the aggregate shock and the resulting propagation through the economy. +The aggregate endowment shock $d_{a,t}$ follows an AR(1) process with $\rho_1 = 0.95$, so after the initial impact it decays monotonically toward zero as $0.95^t$. -If $d_{a,t}$ shows clear AR(2) dynamics, this confirms the aggregate endowment shock propagates correctly through the closed-loop system. +Capital, by contrast, is an endogenous stock that accumulates when the planner smooths consumption. -The responses in consumption, investment, and capital reflect the planner's optimal smoothing behavior given the Hall-style preferences with adjustment costs. +The positive endowment shock increases resources temporarily, but Hall-style preferences imply the planner saves part of the windfall rather than consuming it immediately. -```{code-cell} ipython3 -# Set time offset for DMD analysis (after burn-in period) -t0 = burn_in +This causes capital to rise as $d_{a,t}$ falls — this is permanent income logic at work. + +Next, we examine if the household consumption and endowment paths generated by the simulation obey the Gorman sharing rule +```{code-cell} ipython3 # Now stack the household consumption panel with household endowments and rerun DMD. c_j_t0 = paths["c_j"][..., t0:] d_j_t0 = paths["d_share"][..., t0:] @@ -1619,174 +1629,68 @@ plt.tight_layout() plt.show() ``` -```{code-cell} ipython3 -def make_state_labels(n, n_absorb=None, latex=False): - """Create labels for state vector x_t = [h_{t-1}, k_{t-1}, z_t].""" - if n_absorb is None: - n_absorb = max(1, n // 10) - - if latex: - base = ["$h_{t-1}$", "$k_{t-1}$", "const", "$d_{a,t}$", "$d_{a,t-1}$"] - etas = [fr"$\eta_{{{j+n_absorb+1}}}$" for j in range(n - n_absorb)] - xis = [fr"$\xi_{{{j+1}}}$" for j in range(n)] - return base + etas + xis - - base = ["h_{t-1}", "k_{t-1}", "const", "d_a,t", "d_a,t-1"] - etas = [f"eta_{j+n_absorb+1}" for j in range(n - n_absorb)] - xis = [f"xi_{j+1}" for j in range(n)] - return base + etas + xis - -Sc = econ.Sc -sc = Sc.reshape(-1) -nx = sc.size - -x_labels = make_state_labels(N, n_absorb=n_absorb) - -tol = 1e-12 -nz_idx = np.where(np.abs(sc) > tol)[0] -print(f"c_t = Sc x_t has {len(nz_idx)} nonzero coefficients (out of {nx})") - -topk = 15 -top_idx = nz_idx[np.argsort(np.abs(sc[nz_idx]))[::-1][:topk]] -print("\nTop |Sc| coefficients:") -for idx in top_idx: - print(f" {x_labels[idx]:>20s}: {sc[idx]: .6g}") - -idx_eta_start = 5 -idx_eta_end = 5 + (N - n_absorb) -idx_xi_start = idx_eta_end -idx_xi_end = idx_xi_start + N - -eta_coef = sc[idx_eta_start:idx_eta_end] -xi_coef = sc[idx_xi_start:idx_xi_end] - -print(f"\nIdiosyncratic endowment shocks: min={np.min(eta_coef):.3g}, max={np.max(eta_coef):.3g}") -print(f"Preference shocks: min={np.min(xi_coef):.3g}, max={np.max(xi_coef):.3g}") - -xp_trim = paths["x_path"][:, burn_in:] -c_path = (Sc @ xp_trim).squeeze() -c_dm = c_path - c_path.mean() - -def r2_from_state_indices(idxs): - Xg = xp_trim[idxs, :].T - Xg = Xg - Xg.mean(axis=0, keepdims=True) - y = c_dm - beta_hat = np.linalg.lstsq(Xg, y, rcond=None)[0] - y_hat = Xg @ beta_hat - resid = y - y_hat - return 1 - (np.var(resid) / np.var(y)) - -groups = { - "capital only (k_{t-1})": [1], - "aggregate endow (d_a,t, d_a,t-1)": [3, 4], - "idio endow only (all eta_j)": list(range(idx_eta_start, idx_eta_end)), - "pref shocks only (all xi_j)": list(range(idx_xi_start, idx_xi_end)), - "all z_t": list(range(2, nx)), -} - -print("\nR^2 of c_t (demeaned) explained by state blocks:") -for name, idxs in groups.items(): - print(f" {name:<35s}: {r2_from_state_indices(idxs):.4f}") -``` - -```{code-cell} ipython3 -print( -econ.Sc.shape, # c_t -econ.Sg.shape, # g_t -econ.Si.shape, # i_t -econ.Sh.shape, # h_t -econ.Sk.shape # k_t -) -``` +Indeed, the average of individual household endowments tracks the aggregate endowment process, confirming that the construction of the idiosyncratic shocks and absorbing households works as intended. -```{code-cell} ipython3 -xp = paths["x_path"] -T_plot2 = 250 - -fig, ax = plt.subplots(figsize=(10, 6)) -ax.plot(xp[1, burn_in:burn_in+T_plot2], label=r"$k_{t-1}$", lw=2) -if xp.shape[0] > 3: - ax.plot(xp[3, burn_in:burn_in+T_plot2], label=r"$d_{0, t}$", lw=2) -ax.set_xlabel("time") -ax.legend() -plt.tight_layout() -plt.show() -``` ## Redistribution via Pareto weight reallocation This section analyzes tax-and-transfer schemes by reinterpreting competitive equilibrium allocations in terms of Pareto weights, then considering alternative weight distributions that redistribute consumption while preserving aggregate dynamics. -### Competitive equilibrium and Pareto weights - -Start with the competitive equilibrium consumption allocation described by equation (12.4.4) on page 264 of Hansen and Sargent (2013): +### Redistribution via Pareto weights -$$ -c_{jt} - \chi_{jt} = (u_j/u_a) (c_{at} - \chi_{at}), -$$ - -where: -- The $a$ subscript pertains to the representative agent -- $c_{at}$ and $\chi_{at}$ are computed from the representative agent problem -- The $j$ subscript pertains to the $j$th household -- $c_{jt}$ and $\chi_{jt}$ are computed via the Gorman aggregation framework (the `heter` function above) +The sharing rule {eq}`eq:sharing_rule` can be written as $c_{jt} - \chi_{jt} = \mu_j (c_t - \chi_t)$, where $\mu_j$ is household $j$'s wealth share. Define the Pareto weight $\lambda_j := \mu_j$, with $\sum_{j=1}^J \lambda_j = 1$. -The Gorman sharing rule gives $c_{jt} = \mu_j c_t + \tilde{\chi}_{jt}$ where $\mu_j := u_j/u_a$ is household $j$'s wealth share. +Consider redistributing consumption by choosing new weights $\{\lambda_j^*\}$ satisfying $\lambda_j^* \geq 0$ and $\sum_j \lambda_j^* = 1$. The new allocation $c_{jt} - \chi_{jt} = \lambda_j^* (c_t - \chi_t)$ preserves aggregate dynamics $(c_t, k_t)$ while reallocating consumption across households. -Define the Pareto weight $\lambda_j := \mu_j$, determined by initial wealth distribution. +### Post-tax-and-transfer consumption and income -These weights sum to unity: $\sum_{j=1}^J \lambda_j = 1$, following from the representative agent construction. +Let $R := \delta_k + \gamma_1$. -With this notation, household consumption net of deviation terms is proportional to aggregate consumption: +For a given Pareto-weight vector $\omega$, +household $j$'s assets are $a_{j,t} \equiv \omega_j k_t + \hat{k}_{j,t}$ and income is $$ -c_{jt} - \chi_{jt} = \lambda_j (c_{at} - \chi_{at}). +y_{j,t}(\omega) = \omega_j d_t + (R-1) a_{j,t-1}. $$ -### Redistribution proposal +In this section, we compare pre- and post-redistribution consumption and income. -Consider an efficient tax-and-transfer scheme that leaves aggregate consumption and capital accumulation unaltered but redistributes incomes in a way consistent with a new set of Pareto weights $\{\lambda_j^*\}_{j=1}^J$ satisfying: +Let $\mu := \{\mu_j\}_{j=1}^J$ denote the original competitive equilibrium Pareto weights, and let $\lambda^* := \{\lambda_j^*\}_{j=1}^J$ denote the redistributed weights. -$$ -\lambda_j^* \geq 0 \quad \forall j, \qquad \sum_{j=1}^J \lambda_j^* = 1. -$$ +We write $y^{pre}_{j,t} = y_{j,t}(\mu)$ and $y^{post}_{j,t} = y_{j,t}(\lambda^*)$, then compare consumption to income using percentile plots. -The associated competitive equilibrium consumption allocation under the new Pareto weights is: +To implement a redistribution from $\mu$ to $\lambda^*$, we construct new Pareto weights $\{\lambda_j^*\}_{j=1}^J$ that: -$$ -c_{jt} - \chi_{jt} = \lambda_j^* (c_{at} - \chi_{at}). -$$ +1. Lower the weights for low-$j$ types (wealthier households in our ordering) +2. Increase the weights for high-$j$ types (less wealthy households) +3. Leave middle-$j$ types relatively unaffected +4. Preserve the constraint $\sum_{j=1}^J \lambda_j^* = 1$ -Since the aggregate allocation $(c_{at}, k_{at}, \chi_{at})$ is unchanged, this redistribution preserves efficiency while reallocating consumption across households according to the new weights. - -### Post-tax-and-transfer consumption and income - -We construct household private income from endowments and net asset returns. - -Let $R := \delta_k + \gamma$ and $a_{j,t}$ denote household $j$'s total assets. - -Define pre-tax income as: +We implement this using a smooth transformation. Let $\{\lambda_j\}_{j=1}^J$ denote the original competitive equilibrium Pareto weights (sorted in descending order). Define the redistribution function: $$ -y^{pre}_{j,t} := d_{j,t} + (R-1)a_{j,t-1}. +f(j; J) = \frac{j-1}{J-1}, \qquad +g(j; J) = 2\left|f(j; J) - \frac{1}{2}\right|, \qquad +\tau(j; J, \alpha, \beta) = \alpha \cdot \left[g(j; J)\right]^\beta, $$ -We compare consumption to income using percentile plots. - -To implement redistribution, we construct new Pareto weights $\{\lambda_j^*\}_{j=1}^J$ using a smooth transformation that shifts weight from high-wealth to low-wealth households while preserving $\sum_{j=1}^J \lambda_j^* = 1$. +where: +- $j \in \{1, \ldots, J\}$ is the household index +- $\alpha > 0$ controls the overall magnitude of redistribution (with $\tau$ maximized at the extremes) +- $\beta > 1$ controls the progressivity (higher $\beta$ concentrates redistribution more strongly in the tails) -Define the redistribution function: +The redistributed Pareto weights are: $$ -\tau(j; \alpha, \beta) = \alpha \cdot \left[2\left|\frac{j-1}{J-1} - \frac{1}{2}\right|\right]^\beta, +\tilde{\lambda}_j = \lambda_j + \tau(j; J, \alpha, \beta) \cdot (\bar{\lambda} - \lambda_j), $$ -where $\alpha$ controls magnitude and $\beta$ controls progressivity. +where $\bar{\lambda} = J^{-1}$ is the equal-weight benchmark. -The redistributed weights are: +To ensure the weights sum to unity, we normalize: $$ -\lambda_j^* = \frac{\lambda_j + \tau(j) \cdot (J^{-1} - \lambda_j)}{\sum_{k} [\lambda_k + \tau(k) \cdot (J^{-1} - \lambda_k)]}. +\lambda_j^* = \frac{\tilde{\lambda}_j}{\sum_{k=1}^J \tilde{\lambda}_k}. $$ ```{code-cell} ipython3 @@ -1808,9 +1712,7 @@ def create_redistributed_weights(λ_orig, α=0.5, β=2.0): return λ_star ``` -```{code-cell} ipython3 -paths["μ"] -``` +Let's plot the effect of redistribution on the Pareto weights in our 100-household economy ```{code-cell} ipython3 μ_values = paths["μ"] @@ -1848,33 +1750,16 @@ plt.tight_layout() plt.show() ``` +Once we have the redistributed Pareto weights, we can compute the new household consumption and income paths. + +The function below computes household consumption and income under given Pareto weights + ```{code-cell} ipython3 def allocation_from_weights(paths, econ, U_b_list, weights, γ_1, Λ, h0i=None): """ Compute household consumption and income under given Pareto weights. - - Parameters - ---------- - paths : dict - Dictionary containing aggregate paths (x_path, c, d, k) - econ : DLE - DLE economy object - U_b_list : list - List of household-specific bliss matrices - weights : array - Pareto weight vector for households - γ_1 : float - Capital productivity parameter - Λ : float or array - Durable service flow parameter - h0i : array, optional - Initial household durable stock - - Returns - ------- - dict - Dictionary with household consumption 'c' and net income 'y_net' """ + weights = np.asarray(weights).reshape(-1) N = len(weights) @@ -1942,28 +1827,8 @@ def allocation_from_weights(paths, econ, U_b_list, weights, γ_1, Λ, h0i=None): return {"c": c_j, "y_net": y_net, "χ_tilde": χ_tilde, "k_hat": k_hat, "a_total": a_total} ``` -Let $R = \delta_k + \gamma_1$. For a given Pareto-weight vector $\omega$ (pre: $\omega=\mu$; post: $\omega=\lambda^*$), define household assets -$$ -a_{j,t} \equiv \omega_j k_t + \hat{k}_{j,t}. -$$ - -The income measure we use is -$$ -y_{j,t}(\omega) -= \omega_j d_t + (R-1)a_{j,t-1} -= \omega_j d_t + (R-1)\big(\omega_j k_{t-1} + \hat{k}_{j,t-1}\big). -$$ - -So -$$ -y^{pre}_{j,t} = y_{j,t}(\mu), -\qquad -y^{post}_{j,t} = y_{j,t}(\lambda^*). -$$ - ```{code-cell} ipython3 μ_values = np.asarray(paths["μ"]).reshape(-1) -t0 = burn_in R = float(δ_k + γ_1) From 5a13cd168d20cdc7821f209a4b000a48f6179ec1 Mon Sep 17 00:00:00 2001 From: Humphrey Yang Date: Thu, 25 Dec 2025 13:05:28 +1100 Subject: [PATCH 06/17] updates --- lectures/gorman_heterogeneous_households.md | 43 +++++++++++++++++---- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/lectures/gorman_heterogeneous_households.md b/lectures/gorman_heterogeneous_households.md index f392f4bf..afaaecfc 100644 --- a/lectures/gorman_heterogeneous_households.md +++ b/lectures/gorman_heterogeneous_households.md @@ -379,6 +379,8 @@ $$ \left[(s_t - b_t)^\top(s_t - b_t) + g_t^\top g_t\right] $$ (eq:planner_objective) +where $g_t = \sum_j \ell_{jt}$ is the aggregate "intermediate good" that represents total labor supply in the DLE formulation. + subject to technology constraints $$ @@ -685,7 +687,9 @@ At time zero, household $j$ executes the following trades: 2. Household $j$ purchases $\mu_j$ shares of all securities (equivalently, $\mu_j$ shares of a mutual fund holding the aggregate endowment). 3. Household $j$ takes position $\hat{k}_{j0}$ in the one-period bond. -After this initial rebalancing, household $j$ holds the portfolio forever. +After this initial rebalancing, household $j$ maintains a constant risky portfolio share $\mu_j$ forever, while using the one-period bond for dynamic rebalancing. + +The bond position $\hat{k}_{jt}$ evolves each period according to the recursion derived below. The portfolio weight $\mu_j$ is not arbitrary: it is the unique weight that allows the limited-markets portfolio to replicate the Arrow-Debreu consumption allocation. @@ -710,13 +714,17 @@ which is the aggregate endowment. If household $j$ holds fraction $\theta_j$ of this fund, it receives $\theta_j d_t$ in dividends. -The proportional part of consumption $\mu_j c_t$ must be financed by the mutual fund and capital holdings. Since the aggregate resource constraint is +The proportional part of consumption $\mu_j c_t$ must be financed by the mutual fund and capital holdings. + +Using our calibration ($\Phi_c = \Phi_i = \theta_k = 1$, $\Gamma = \gamma_1$), the resource constraint becomes $c_t + i_t = \gamma_1 k_{t-1} + d_t$ and capital accumulation is $k_t = \delta_k k_{t-1} + i_t$. + +Substituting $i_t = k_t - \delta_k k_{t-1}$ into the resource constraint gives: $$ -c_t + i_t = (\delta_k + \gamma_1) k_{t-1} + d_t, +c_t + k_t = (\delta_k + \gamma_1) k_{t-1} + d_t. $$ -holding $\theta_j$ shares of aggregate output (capital income plus endowments) delivers $\theta_j [(\delta_k + \gamma_1)k_{t-1} + d_t]$. +Holding $\theta_j$ shares of aggregate wealth (capital plus claims to endowments) delivers $\theta_j [(\delta_k + \gamma_1)k_{t-1} + d_t] = \theta_j (c_t + k_t)$. For this to finance $\mu_j c_t$ plus reinvestment $\mu_j k_t$, we need $\theta_j = \mu_j$. @@ -736,6 +744,10 @@ $$ where $R := \delta_k + \gamma_1$ is the gross return. +```{note} +The constant gross return $R = \delta_k + \gamma_1$ arises from our specific calibration ($\Phi_c = \Phi_i = \Theta_k = 1$, $\Gamma = \gamma_1$). +``` + Substituting the sharing rule by replacing $c_{jt}$ with $\mu_j c_t + \tilde{\chi}_{jt}$ gives: $$ @@ -924,6 +936,8 @@ def compute_household_paths(econ, U_b_list, U_d_list, x0, x_path, γ_1, Λ, h0i= η = np.zeros((n_h, T + 1)) η[:, 0] = np.asarray(h0i).reshape(-1) + # At t=0, assume η_{-1} = 0 + χ_tilde[j, 0] = (Π_inv @ b_tilde[:, 0]).squeeze() for t in range(1, T): χ_tilde[j, t] = (-Π_inv @ Λ @ η[:, t - 1] + Π_inv @ b_tilde[:, t]).squeeze() η[:, t] = (A_h @ η[:, t - 1] + B_h @ b_tilde[:, t]).squeeze() @@ -939,7 +953,7 @@ def compute_household_paths(econ, U_b_list, U_d_list, x0, x_path, γ_1, Λ, h0i= # Capital share in mutual fund k_share[j] = (μ[j] * k[0]).squeeze() - # Solving initial bond position and path + # Solve for bond path by backward iteration from terminal condition. if abs(R - 1.0) >= 1e-14: k_hat[j, -1] = χ_tilde[j, -1] / (R - 1.0) for t in range(T - 1, 0, -1): @@ -947,6 +961,13 @@ def compute_household_paths(econ, U_b_list, U_d_list, x0, x_path, γ_1, Λ, h0i= a_total[j] = k_share[j] + k_hat[j] + # Validate that Gorman weights sum to 1 (required for sharing rule consistency) + μ_sum = np.sum(μ) + if abs(μ_sum - 1.0) > 1e-6: + import warnings + warnings.warn(f"Gorman weights μ sum to {μ_sum:.6f}, not 1.0. " + "This may indicate calibration issues.") + return { "μ": μ, "χ_tilde": χ_tilde, @@ -1519,8 +1540,13 @@ G = np.vstack([ econ.Ss, ]) +# Extract dimensions for proper slicing +n_h = np.atleast_2d(econ.thetah).shape[0] +n_k = np.atleast_2d(econ.thetak).shape[0] +n_endo = n_h + n_k # endogenous state dimension + print(f"Shapes: A0 {A0.shape}, C {C.shape}, G {G.shape}") -print(f"max |A0[2:,2:] - A22| = {np.max(np.abs(A0[2:, 2:] - A22)):.2e}") +print(f"max |A0[{n_endo}:,{n_endo}:] - A22| = {np.max(np.abs(A0[n_endo:, n_endo:] - A22)):.2e}") ``` With the state space representation, we can compute impulse responses to show how shocks propagate through the economy. @@ -1801,14 +1827,15 @@ def allocation_from_weights(paths, econ, U_b_list, weights, γ_1, Λ, h0i=None): η = np.zeros((n_h, T + 1)) η[:, 0] = np.asarray(h0i).reshape(-1) - + # At t=0, assume η_{-1} = 0 + χ_tilde[j, 0] = (Π_inv @ b_tilde[:, 0]).squeeze() for t in range(1, T): χ_tilde[j, t] = (-Π_inv @ Λ @ η[:, t - 1] + Π_inv @ b_tilde[:, t]).squeeze() η[:, t] = (A_h @ η[:, t - 1] + B_h @ b_tilde[:, t]).squeeze() c_j[j] = (weights[j] * c_agg[0] + χ_tilde[j]).squeeze() - # Compute bond position + # Solve for bond path by backward iteration from terminal condition. if abs(R - 1.0) >= 1e-14: k_hat[j, -1] = χ_tilde[j, -1] / (R - 1.0) for t in range(T - 1, 0, -1): From c8a8d05ecb19d4c7bdfcb6ce4bb77fa5a6e7410c Mon Sep 17 00:00:00 2001 From: Humphrey Yang Date: Thu, 25 Dec 2025 23:46:21 +1100 Subject: [PATCH 07/17] updates --- lectures/_static/quant-econ.bib | 14 +- lectures/gorman_heterogeneous_households.md | 137 ++++++++++++-------- 2 files changed, 91 insertions(+), 60 deletions(-) diff --git a/lectures/_static/quant-econ.bib b/lectures/_static/quant-econ.bib index 73304061..9e72146a 100644 --- a/lectures/_static/quant-econ.bib +++ b/lectures/_static/quant-econ.bib @@ -1733,13 +1733,13 @@ @article{KydlandPrescott1977 } @article{KydlandPrescott1980, - author = {Kydland, Finn E., and Edward C. Prescott}, - journal = {Econometrics}, - pages = {1345-2370}, - title = {Time to Build and Aggregate Fluctuations}, - volume = {50}, - number = {6}, - year = {1980} + title={Dynamic Optimal Taxation, Rational Expectations and Optimal Control}, + author={Kydland, Finn E and Prescott, Edward C}, + journal={Journal of Economic Dynamics and Control}, + volume={2}, + pages={79--91}, + year={1980}, + publisher={Elsevier} } @book{LasotaMackey1994, diff --git a/lectures/gorman_heterogeneous_households.md b/lectures/gorman_heterogeneous_households.md index afaaecfc..bff9d684 100644 --- a/lectures/gorman_heterogeneous_households.md +++ b/lectures/gorman_heterogeneous_households.md @@ -77,7 +77,7 @@ With the help of this powerful result, we proceed in three steps in this lecture We then simulate examples with two and many households. -### Gorman aggregation +### Gorman aggregation in a static economy To see where the sharing rule comes from, start with a static economy with $n$ goods, price vector $p$, and consumers $j = 1, \ldots, J$. @@ -127,17 +127,9 @@ $$ The baseline functions $\psi_j$ and the common function $\psi_c$ are the derivatives of concave functions that are positively homogeneous of degree 1. -Hence these functions are homogeneous of degree zero in prices, assuring that the slopes of indifference curves should depend only on the *ratio* of prices. +Hence these functions are homogeneous of degree zero in prices, so indifference curve slopes depend only on price *ratios*. -This is why aggregate allocations and prices can be computed *before* household allocations. - -Mapping $c^a = \psi_a(p) + u^a \psi_c(p)$ can be inverted to obtain a *gradient* vector $p$ that is independent of how utilities are allocated across consumers. - -Since $\psi_c$ and $\psi_a$ are homogeneous of degree zero, gradients are determined only up to a scalar multiple. - -Armed with $p$, we can then allocate utility among $J$ consumers while respecting the adding up constraint. - -The allocation of aggregate consumption across goods and the associated gradient are determined independently of how aggregate utility is divided among consumers. +This homogeneity allows us to invert $c^a = \psi_a(p) + u^a \psi_c(p)$ for the gradient vector $p$ (determined up to scale) and then allocate utility among consumers while respecting adding-up constraints. A decentralized version of this analysis proceeds as follows. @@ -173,6 +165,7 @@ c^j - \chi^j = \frac{u^j}{u^a}\,(c^a - \chi^a), $$ so that there is a common scale factor $(u^j/u^a)$ across all goods for person $j$. + Hence the fraction of total utility assigned to consumer $j$ determines his fraction of the vector $(c^a - \chi^a)$. This is exactly the form we use below, except that goods are indexed by both dates and states. @@ -181,7 +174,7 @@ This is exactly the form we use below, except that goods are indexed by both dat Time is discrete, $t = 0,1,2,\dots$. -Households are indexed by $j$ and we use $N$ for the number of simulated households. +Households are indexed by $j = 1, \ldots, J$, where $J$ denotes the total number of households in the economy. ### Exogenous state @@ -199,7 +192,7 @@ The vector $z_t$ typically contains three types of components. 2. Aggregate shocks: Components with persistent dynamics that affect all households. - - In the examples in {ref}`gorman_twohh`, an AR(2) process drives aggregate endowment fluctuations. + - In {ref}`gorman_twohh`, an AR(2) process drives aggregate endowment fluctuations. 3. Idiosyncratic shocks: Components that enter individual endowments with loadings summing to zero across households. @@ -224,36 +217,39 @@ Aggregates are economy-wide totals summed across households: consumption $c_t$, ### Gorman weight -Let $u^j$ denote the scalar index in the Gorman demand representation above and define +Let $\mu_{0j}^w$ denote household $j$'s *time-zero marginal utility of wealth*, the Lagrange multiplier on its intertemporal budget constraint. + +Define the aggregate multiplier as $$ -\mu_j := \frac{u^j}{u^a}, -\qquad -u^a := \sum_{i=1}^J u^i. +\mu_{0a}^w := \sum_{j=1}^J \mu_{0j}^w. $$ -In the quadratic setting used here, we can choose $u^j$ proportional to household $j$'s *time-zero marginal utility of wealth* (the Lagrange multiplier on its intertemporal budget constraint, denoted $\mu_{0j}^w$ in {cite:t}`HansenSargent2013`), and then normalize so that the $\mu_j$ sum to one. +The ratio $\mu_{0j}^w / \mu_{0a}^w$ serves as household $j$'s Gorman weight. -The sharing rule can be written either in the "baseline" form, +For notational convenience, we write $$ -c_{jt} - \chi_{jt} = \mu_j (c_t - \chi_t), +\mu_j := \frac{\mu_{0j}^w}{\mu_{0a}^w}, $$ -or, equivalently, in the deviation form used in this lecture, +so that $\sum_j \mu_j = 1$. + +The sharing rule can be written either in the "baseline" form, $$ -c_{jt} = \mu_j c_t + \tilde{\chi}_{jt}. +c_{jt} - \chi_{jt} = \mu_j (c_t - \chi_t), $$ -where $\tilde{\chi}_{jt} = \chi_{jt} - \mu_j \chi_t$ represents household-specific deviations from the proportional share. - +or, equivalently, in the deviation form used in this lecture, -The proportional term $\mu_j c_t$ is household $j$'s share of aggregate consumption. +$$ +c_{jt} = \mu_j c_t + \tilde{\chi}_{jt}, \qquad \tilde{\chi}_{jt} := \chi_{jt} - \mu_j \chi_t. +$$ (eq:sharing_rule) -The deviation term $\tilde{\chi}_{jt}$ captures how household $j$'s consumption differs from its proportional share due to preference heterogeneity (bliss points) and initial conditions, and satisfies $\tilde{\chi}_{jt} = \chi_{jt} - \mu_j \chi_t$. +Here $\mu_j c_t$ is household $j$'s proportional share and $\tilde{\chi}_{jt}$ captures deviations due to preference heterogeneity and initial conditions. -These deviations sum to zero across households: $\sum_j \tilde{\chi}_{jt} = 0$. +These deviations sum to zero: $\sum_j \tilde{\chi}_{jt} = 0$. ### Technologies @@ -303,12 +299,17 @@ and the intertemporal budget constraint $$ \mathbb{E}_0 \sum_{t=0}^\infty \beta^t p_{0t} \cdot c_{jt} -= \mathbb{E}_0 \sum_{t=0}^\infty \beta^t (w_{t0} \ell_{jt} + \alpha_{0t} \cdot d_{jt}) + v_0 \cdot k_{j,-1}, += \mathbb{E}_0 \sum_{t=0}^\infty \beta^t (w_{0t} \ell_{jt} + p_{0t} \cdot d_{jt}) + v_0 \cdot k_{j,-1}, $$ (eq:hh_budget) where $b_{jt} = U_b^j z_t$ is household $j$'s preference shock, $d_{jt} = U_d^j z_t$ is household $j$'s endowment stream, $h_{j,-1}$ and $k_{j,-1}$ are given initial household stocks, and $\ell_{jt}$ is household labor supply. -Prices $(p_{0t}, w_{t0}, \alpha_{0t}, v_0)$ are determined in equilibrium. +The prices are: +- $p_{0t}$: the time-0 Arrow-Debreu price vector for date-$t$ consumption goods (across states), so $p_{0t} \cdot c_{jt}$ is the time-0 value of date-$t$ consumption +- $w_{0t}$: the time-0 value of date-$t$ labor +- $v_0$: the time-0 value of initial capital + +These prices are determined in equilibrium. This specification confines heterogeneity among consumers to four sources: @@ -332,19 +333,11 @@ These restrictions enable Gorman aggregation by ensuring that household demands #### From individual problems to the aggregate problem -As we hinted before, Gorman aggregation conditions establish a powerful equivalence: the competitive equilibrium allocation solves a social planner's problem with a specific set of Pareto weights. - -We can solve for aggregate quantities by maximizing a weighted sum of household utilities rather than solving each household's problem separately. - -Equilibrium prices emerge naturally as the shadow prices from the planner's problem. - -When preferences satisfy Gorman's restrictions, the equilibrium allocation solves a planning problem with Pareto weights given by the household wealth multipliers $\mu_{0j}^w$. +Under Gorman's restrictions, the competitive equilibrium allocation solves a social planner's problem with Pareto weights $\mu_j = \mu_{0j}^w / \mu_{0a}^w$. -The problem decomposes into two steps: +Equilibrium prices emerge as the shadow prices from this problem. -1. In the aggregate step, we solve the planner's problem with a representative consumer by summing across all households. - -2. In the allocation step, we use the sharing rule to distribute aggregate consumption to individual households based on their wealth shares and preference shocks. +Next, we formulate the aggregate planning problem. ### The aggregate planning problem @@ -352,7 +345,7 @@ Summing the household budget constraints across $j = 1,\ldots,J$ gives $$ \mathbb{E}_0 \sum_{t=0}^\infty \beta^t p_{0t} \cdot \left(\sum_j c_{jt}\right) -= \mathbb{E}_0 \sum_{t=0}^\infty \beta^t \left(w_{t0} \sum_j \ell_{jt} + \alpha_{0t} \cdot \sum_j d_{jt}\right) + v_0 \cdot \sum_j k_{j,-1}. += \mathbb{E}_0 \sum_{t=0}^\infty \beta^t \left(w_{0t} \sum_j \ell_{jt} + p_{0t} \cdot \sum_j d_{jt}\right) + v_0 \cdot \sum_j k_{j,-1}. $$ (eq:agg_budget_sum) In equilibrium, aggregate consumption $c_t = \sum_j c_{jt}$ must satisfy the economy's resource constraint @@ -408,17 +401,45 @@ These shadow prices correspond to competitive equilibrium prices. ## Household allocations -Under the Gorman sharing rule, household $j$'s consumption is +A direct way to compute allocations to individuals would be to solve the problem each household faces in competitive equilibrium at equilibrium prices. + +For a fixed Lagrange multiplier $\mu_{0j}^w$ on the household's budget constraint, the household's problem can be expressed as an optimal linear regulator with a state vector augmented to reflect aggregate state variables determining scaled Arrow-Debreu prices. + +One could compute individual allocations by iterating to find the multiplier $\mu_{0j}^w$ that satisfies the budget constraint. + +But there is a more elegant approach using Gorman's allocation rules. + +### Allocation rules + +The allocation rule for household $j$'s labor (the "intermediate good" $g_{jt} = \ell_{jt}$) is $$ -c_{jt} = \mu_j c_t + \tilde{\chi}_{jt}, \qquad \mu_j := u_j/u_a. -$$ (eq:sharing_rule) +\ell_{jt} = \mu_j \, \ell_t, +$$ (eq:labor_allocation) + +where $\ell_t = \sum_i \ell_{it}$ is aggregate labor and $\mu_j = \mu_{0j}^w / \mu_{0a}^w$ is the Gorman weight. -The proportional term $\mu_j c_t$ is household $j$'s share of aggregate consumption. +The first-order conditions for consumption services give -The deviation term $\tilde{\chi}_{jt}$ captures deviations driven by preference and endowment heterogeneity. +$$ +s_{jt} - b_{jt} = \mu_{0j}^w \rho_t^0, +$$ (eq:foc_services) -In the first step, we solve household $j$'s deviation problem to obtain $\tilde{\chi}_{jt}$ as a function of its deviation state $\tilde{\eta}_{jt} = h_{j,t-1} - \mu_j h_{t-1}$. +where $\rho_t^0$ is the shadow price of services from the aggregate problem. + +Since the aggregate first-order condition is $s_t - b_t = \mu_{0a}^w \rho_t^0$, we can eliminate the price $\rho_t^0$ to obtain the service allocation rule in deviation form: + +$$ +s_{jt} - b_{jt} = \mu_j (s_t - b_t), +$$ (eq:service_allocation) + +or equivalently $\tilde{s}_{jt} = \tilde{b}_{jt}$, where $\tilde{y}_{jt} := y_{jt} - \mu_j y_t$ for any variable $y$. + +The beauty of this representation is that it does not involve prices directly. + +### Deviation consumption + +Given the sharing rule {eq}`eq:sharing_rule` introduced above, we now solve for household $j$'s deviation term $\tilde{\chi}_{jt}$ as a function of its deviation state $\tilde{\eta}_{jt} = h_{j,t-1} - \mu_j h_{t-1}$. The "deviation" consumption $\tilde{\chi}_{jt}$ satisfies the inverse canonical representation @@ -1027,7 +1048,7 @@ We reproduce the two-household example from Chapter 12.6 of {cite:t}`HansenSarge There are two households, each with preferences $$ --\frac{1}{2} \mathbb{E} \sum_{t=0}^{\infty} \beta^t \left[(c^i_t - b^i_t)^2 + \ell^{i\,2}_t\right] \mid J_0, \quad i = 1, 2. +-\frac{1}{2} \mathbb{E} \sum_{t=0}^{\infty} \beta^t \left[(c^i_t - b^i_t)^2 + \ell^{i\,2}_t\right] \mid \mathcal{J}_0, \quad i = 1, 2. $$ We set $b^i_t = 15$ for $i = 1, 2$, so the aggregate preference shock is $b_t = \sum_i b^i_t = 30$. The endowment processes are @@ -1139,14 +1160,20 @@ ax.legend() plt.show() ``` +### Risk sharing + The Gorman sharing rule has strong implications for risk sharing across households. -Because the weight $\mu_j$ is time-invariant and determined at date zero, the deviation process $\{c_{jt} - \tilde{\chi}_{jt}\}$ exhibits perfect risk pooling: all households share aggregate consumption risk in fixed proportions. +Because the coefficient $\mu_j = \mu_{0j}^w / \mu_{0a}^w$ is invariant over time and across goods, allocation rule {eq}`eq:sharing_rule` implies a form of risk pooling in the deviation process $\{c_{jt} - \chi_{jt}\}$. + +All households share aggregate consumption risk in fixed proportions determined at date zero. -Non-separabilities in preferences (over time or across goods) affect only the baseline process $\{\tilde{\chi}_{jt}\}$ and the calculation of the risk-sharing coefficient $\mu_j$. +Non-separabilities in preferences (either over time or across goods) affect only the construction of the baseline process $\{\chi_{jt}\}$ and the calculation of the risk-sharing coefficient $\mu_j$ implied by the distribution of wealth. They do not break the proportional sharing of aggregate risk. +In the special case where the preference shock processes $\{b_{jt}\}$ are deterministic in the sense that they reside in the information set $\mathcal{J}_0$, individual consumption goods will be perfectly correlated with their aggregate counterparts (conditioned on $\mathcal{J}_0$). + The next figure plots the limited-markets bond adjustment and confirms that the adjustments sum to approximately zero. ```{code-cell} ipython3 @@ -1173,7 +1200,7 @@ The final check is to verify that the bond positions sum to zero at all times, c np.max(np.abs(paths["k_hat"].sum(axis=0))) ``` -## Many-household Gorman economies +## Example: many-household Gorman economies We now extend the two-household example to an economy with many households. @@ -1664,9 +1691,13 @@ This section analyzes tax-and-transfer schemes by reinterpreting competitive equ ### Redistribution via Pareto weights -The sharing rule {eq}`eq:sharing_rule` can be written as $c_{jt} - \chi_{jt} = \mu_j (c_t - \chi_t)$, where $\mu_j$ is household $j$'s wealth share. Define the Pareto weight $\lambda_j := \mu_j$, with $\sum_{j=1}^J \lambda_j = 1$. +The sharing rule {eq}`eq:sharing_rule` can be written as $c_{jt} - \chi_{jt} = \mu_j (c_t - \chi_t)$, where $\mu_j$ is household $j$'s wealth share. + +Define the Pareto weight $\lambda_j := \mu_j$, with $\sum_{j=1}^J \lambda_j = 1$. + +Consider redistributing consumption by choosing new weights $\{\lambda_j^*\}$ satisfying $\lambda_j^* \geq 0$ and $\sum_j \lambda_j^* = 1$. -Consider redistributing consumption by choosing new weights $\{\lambda_j^*\}$ satisfying $\lambda_j^* \geq 0$ and $\sum_j \lambda_j^* = 1$. The new allocation $c_{jt} - \chi_{jt} = \lambda_j^* (c_t - \chi_t)$ preserves aggregate dynamics $(c_t, k_t)$ while reallocating consumption across households. +The new allocation $c_{jt} - \chi_{jt} = \lambda_j^* (c_t - \chi_t)$ preserves aggregate dynamics $(c_t, k_t)$ while reallocating consumption across households. ### Post-tax-and-transfer consumption and income From 7ca95130887cb6332444efb22378adc09b1b5dae Mon Sep 17 00:00:00 2001 From: Humphrey Yang Date: Fri, 26 Dec 2025 14:29:53 +1100 Subject: [PATCH 08/17] updates --- lectures/gorman_heterogeneous_households.md | 613 +++++++++++++------- 1 file changed, 402 insertions(+), 211 deletions(-) diff --git a/lectures/gorman_heterogeneous_households.md b/lectures/gorman_heterogeneous_households.md index bff9d684..7ecfade4 100644 --- a/lectures/gorman_heterogeneous_households.md +++ b/lectures/gorman_heterogeneous_households.md @@ -31,7 +31,7 @@ It complements {doc}`hs_recursive_models`, {doc}`growth_in_dles`, and {doc}`irfs The headline result is that a complete-markets allocation can be implemented with a mutual fund and a one-period bond when Gorman aggregation holds. -In addition to what's in Anaconda, this lecture uses the `quantecon` library. +In addition to what's in Anaconda, this lecture uses the `quantecon` library ```{code-cell} ipython3 :tags: [hide-output] @@ -39,7 +39,7 @@ In addition to what's in Anaconda, this lecture uses the `quantecon` library. !pip install --upgrade quantecon ``` -We make the following imports. +We make the following imports ```{code-cell} ipython3 import numpy as np @@ -51,9 +51,9 @@ import matplotlib.pyplot as plt ## Overview -Gorman aggregation lets us solve heterogeneous-household economies in two steps: solve a representative-agent linear-quadratic planning problem for aggregates, then recover household allocations via a wealth-share rule with household-specific deviation terms. +Gorman aggregation lets us solve heterogeneous-household economies in two steps: solve a representative-agent linear-quadratic planning problem for aggregates, then recover household allocations via a "sharing rule" with household-specific deviation terms. -The key insight is that Gorman conditions ensure all consumers have parallel Engel curves, so aggregate allocations and prices can be determined independently of distribution. +Gorman conditions ensure all consumers have parallel Engel curves, so aggregate allocations and prices can be determined independently of distribution. This eliminates the standard problem where the utility possibility frontier shifts with endowment changes, making it impossible to rank allocations without specifying distributional weights. @@ -77,6 +77,8 @@ With the help of this powerful result, we proceed in three steps in this lecture We then simulate examples with two and many households. +Before looking into those details, we first introduce Gorman aggregation in a static economy. + ### Gorman aggregation in a static economy To see where the sharing rule comes from, start with a static economy with $n$ goods, price vector $p$, and consumers $j = 1, \ldots, J$. @@ -103,33 +105,97 @@ This means that for a given aggregate endowment, the competitive equilibrium pri It follows that for this economy, one cannot determine equilibrium prices independently of the equilibrium allocation. +The crossing of the utility possibility frontiers can be seen by plotting the efficient utility pairs for each aggregate endowment + +```{code-cell} ipython3 +--- +mystnb: + figure: + caption: utility possibility frontiers cross when aggregation fails + name: fig-gorman-utility-frontier-crossing +--- +def frontier(X_total, Y_total, grid_size=500): + """ + Compute a utility possibility frontier by tracing the contract curve. + + Preferences: + U^A = X_A^(1/3) Y_A^(2/3) + U^B = X_B^(2/3) Y_B^(1/3) + """ + xA = np.linspace(1e-6, X_total - 1e-6, grid_size) + yA = (4 * xA * Y_total) / (X_total + 3 * xA) # contract curve + xB, yB = X_total - xA, Y_total - yA + + UA = (xA ** (1 / 3)) * (yA ** (2 / 3)) + UB = (xB ** (2 / 3)) * (yB ** (1 / 3)) + return UA, UB + + +UA1, UB1 = frontier(8.0, 3.0) +UA2, UB2 = frontier(3.0, 8.0) + +fig, ax = plt.subplots() +ax.plot(UA1, UB1, lw=2, label=r"$E=(8,3)$") +ax.plot(UA2, UB2, lw=2, label=r"$E=(3,8)$") +ax.set_xlabel("utility of agent A") +ax.set_ylabel("utility of agent B") +ax.legend() +plt.show() +``` + {cite:t}`gorman1953community` described restrictions on preferences under which it *is* possible to obtain a community preference ordering. Whenever Gorman's conditions are satisfied, there occur substantial simplifications in solving multiple-consumer optimal resource allocation problems: in intertemporal contexts, it becomes possible first to determine the optimal allocation of aggregate resources over time, and then allocate aggregate consumption among consumers by assigning utility levels to each person. -Gorman's aggregation conditions amount to assuming each consumer's compensated demand can be written as an affine function of a *scalar* utility index: +There is flexibility in the choice of monotonic transformations that we can use to represent utility levels. + +We restrict the utility index to be either nonnegative or nonpositive. + +In both cases, we define consumer-specific baseline indifference curves $\psi_j(p)$ associated with a utility level of zero. + +The functions $\psi_j$ are gradients of concave functions that are positively homogeneous of degree one. + +To represent a common departure for all consumers from their baseline indifference curves, we introduce a function $\psi_c(p)$. + +This lets the compensated demand function for consumer $j$ be represented as $$ c^j = \psi_j(p) + u^j \psi_c(p), $$ -where $\psi_c(p)$ is common across consumers and $\psi_j(p)$ is consumer-specific. +where $u^j$ is a scalar utility index for consumer $j$. + +The function $\psi_c$ is the gradient of a positively homogeneous of degree one function that is concave when the utility indices are restricted to be nonnegative and convex when they are restricted to be nonpositive. + +It follows that $\psi_j$ and $\psi_c$ are homogeneous of degree zero in prices, which means that the implied indifference curves depend only on ratios of prices to an arbitrarily chosen numeraire. -Aggregating over consumers gives a representative-consumer demand system +The baseline indifference curves are either the highest or lowest indifference curves, corresponding respectively to cases in which the utility indices $u^j$ are restricted to be nonpositive or nonnegative. + +As noted by Gorman, when preferences are of this form, there is a well-defined compensated demand function for a fictitious representative consumer obtained by aggregating individual demands: $$ c^a = \psi_a(p) + u^a \psi_c(p), -\qquad -u^a = \sum_{j=1}^J u^j, -\qquad -\psi_a(p) = \sum_{j=1}^J \psi_j(p). $$ -The baseline functions $\psi_j$ and the common function $\psi_c$ are the derivatives of concave functions that are positively homogeneous of degree 1. +where + +$$ +u^a = \sum_{j=1}^J u^j \quad \text{and} \quad \psi_a(p) = \sum_{j=1}^J \psi_j(p). +$$ (eq:agg_demand) + +In this case, optimal resource allocation in a heterogeneous consumer economy simplifies as follows. + +Preferences for aggregate consumption define a community preference ordering. + +This preference ordering can be combined with a specification of the technology for producing consumption goods to determine the optimal allocation of aggregate consumption. + +The mapping $c^a = \psi_a(p) + u^a \psi_c(p)$ can be inverted to obtain a gradient vector $p$ that is independent of how utilities are allocated across consumers. + +Since $\psi_c$ and $\psi_a$ are homogeneous of degree zero, gradients are determined only up to a scalar multiple. -Hence these functions are homogeneous of degree zero in prices, so indifference curve slopes depend only on price *ratios*. +Armed with $p$, we can then allocate utility among $J$ consumers while respecting the adding-up constraint {eq}`eq:agg_demand`. -This homogeneity allows us to invert $c^a = \psi_a(p) + u^a \psi_c(p)$ for the gradient vector $p$ (determined up to scale) and then allocate utility among consumers while respecting adding-up constraints. +The allocation of aggregate consumption across goods and the associated gradient are determined independently of how aggregate utility is divided among consumers. A decentralized version of this analysis proceeds as follows. @@ -156,9 +222,23 @@ Notice that the coefficient on $W^j$ is the same for all $j$ since $\psi_c(p)/(p The individual allocations can be determined from the Engel curves by substituting for $p$ the gradient vector obtained from the representative consumer's optimal allocation problem. -In the quadratic specifications used in this lecture (and in {cite}`HansenSargent2013`), the baseline components are degenerate in the sense that $\psi_j(p) = \chi^j$ is independent of $p$, where $\chi^j$ is a consumer-specific bliss point represented by a vector with the same dimension as $c^j$. +In the quadratic specifications used in this lecture (and in {cite:t}`HansenSargent2013`), the baseline components are degenerate in the sense that $\psi_j(p) = \chi^j$ is independent of $p$, where $\chi^j$ is a consumer-specific bliss point represented by a vector with the same dimension as $c^j$. -In that case, the static sharing rule reduces to +In that case, the demand functions simplify to + +$$ +c^j = \chi^j + u^j \psi_c(p), \qquad c^a = \chi^a + u^a \psi_c(p), +$$ + +where $\chi^a = \sum_j \chi^j$. + +Solving the aggregate equation for the common term $\psi_c(p) = (c^a - \chi^a)/u^a$ and substituting into the individual equation gives + +$$ +c^j = \chi^j + u^j \cdot \frac{c^a - \chi^a}{u^a}, +$$ + +which rearranges to the static sharing rule $$ c^j - \chi^j = \frac{u^j}{u^a}\,(c^a - \chi^a), @@ -168,12 +248,19 @@ so that there is a common scale factor $(u^j/u^a)$ across all goods for person $ Hence the fraction of total utility assigned to consumer $j$ determines his fraction of the vector $(c^a - \chi^a)$. -This is exactly the form we use below, except that goods are indexed by both dates and states. +This is exactly the form we will see in {eq}`eq:sharing_rule` below, except that goods are indexed by both dates and states in subscripts. + +In the dynamic setting, we set the utility index $u^j$ equal to household $j$'s time-zero marginal utility of wealth $\mu_{0j}^w$, the Lagrange multiplier on the intertemporal budget constraint. + +The ratio $\mu_{0j}^w / \mu_{0a}^w$ (where $\mu_{0a}^w = \sum_j \mu_{0j}^w$) then serves as the time-invariant Gorman weight that determines household $j$'s share of aggregate consumption in excess of baseline. + ## Set up Time is discrete, $t = 0,1,2,\dots$. +All households share a common discount factor $\beta \in (0,1)$. + Households are indexed by $j = 1, \ldots, J$, where $J$ denotes the total number of households in the economy. ### Exogenous state @@ -186,6 +273,8 @@ $$ where $A_{22}$ governs persistence and $C_2$ maps i.i.d. shocks $w_{t+1}$ into the state. +Here we take $\mathbb{E}[w_{t+1}] = 0$ and $\mathbb{E}[w_{t+1} w_{t+1}^\top] = I$. + The vector $z_t$ typically contains three types of components. 1. Constant: The first element is set to 1 and remains constant. @@ -198,59 +287,9 @@ The vector $z_t$ typically contains three types of components. - These generate cross-sectional heterogeneity while preserving aggregate resource constraints. -The selection matrices $U_b$ and $U_d$ pick out which components of $z_t$ affect +The selection matrices $U_b$ and $U_d$ pick out which components of $z_t$ affect household preferences (bliss points) and endowments. -### Aggregate planner state - -The aggregate planner state stacks lagged endogenous stocks and current exogenous variables: - -$$ -x_t = [h_{t-1}^\top, k_{t-1}^\top, z_t^\top]^\top. -$$ - -Here $h_{t-1}$ is the lagged household durable stock (habits or durables affecting utility), $k_{t-1}$ is lagged physical capital, and $z_t$ is the current exogenous state. - -Together, $x_t$ contains everything the planner needs to make decisions at time $t$. - -Aggregates are economy-wide totals summed across households: consumption $c_t$, investment $i_t$, capital $k_t$, household stock $h_t$, service flow $s_t$, intermediate good $g_t$, bliss $b_t$, and endowment $d_t$. - -### Gorman weight - -Let $\mu_{0j}^w$ denote household $j$'s *time-zero marginal utility of wealth*, the Lagrange multiplier on its intertemporal budget constraint. - -Define the aggregate multiplier as - -$$ -\mu_{0a}^w := \sum_{j=1}^J \mu_{0j}^w. -$$ - -The ratio $\mu_{0j}^w / \mu_{0a}^w$ serves as household $j$'s Gorman weight. - -For notational convenience, we write - -$$ -\mu_j := \frac{\mu_{0j}^w}{\mu_{0a}^w}, -$$ - -so that $\sum_j \mu_j = 1$. - -The sharing rule can be written either in the "baseline" form, - -$$ -c_{jt} - \chi_{jt} = \mu_j (c_t - \chi_t), -$$ - -or, equivalently, in the deviation form used in this lecture, - -$$ -c_{jt} = \mu_j c_t + \tilde{\chi}_{jt}, \qquad \tilde{\chi}_{jt} := \chi_{jt} - \mu_j \chi_t. -$$ (eq:sharing_rule) - -Here $\mu_j c_t$ is household $j$'s proportional share and $\tilde{\chi}_{jt}$ captures deviations due to preference heterogeneity and initial conditions. - -These deviations sum to zero: $\sum_j \tilde{\chi}_{jt} = 0$. - ### Technologies The economy's resource constraint is @@ -269,6 +308,16 @@ b_t = U_b z_t, \quad d_t = U_d z_t. $$ +Here $h_t$ is the aggregate household service, +$s_t$ is the aggregate service flow derived from household capital, +$c_t$ is aggregate consumption, $i_t$ is investment, $k_t$ is the aggregate physical capital stock, $d_t$ is the aggregate endowment (or dividend) stream, and $g_t$ is the aggregate "intermediate good" (interpreted as total labor supply in this lecture). + +The matrices $\Phi_c, \Phi_g, \Phi_i$ and $\Gamma$ allow for multiple goods and a general linear mapping from stocks and endowments into resources. + +In the one-good case used in the limited-markets section, these reduce to scalars and the resource constraint becomes $c_t + i_t = \gamma_1 k_{t-1} + d_t$. + +We write $\Pi_h$ for the household service-technology matrix $\Pi$ in {cite:t}`HansenSargent2013`. + Selection matrices such as $S_{(q)}$ map the aggregate state $x_t$ into aggregate quantities such as $q_t = S_{(q)} x_t$ for $q = c, i, k, h, s, g, b, d$. @@ -299,10 +348,13 @@ and the intertemporal budget constraint $$ \mathbb{E}_0 \sum_{t=0}^\infty \beta^t p_{0t} \cdot c_{jt} -= \mathbb{E}_0 \sum_{t=0}^\infty \beta^t (w_{0t} \ell_{jt} + p_{0t} \cdot d_{jt}) + v_0 \cdot k_{j,-1}, += \mathbb{E}_0 \sum_{t=0}^\infty \beta^t (w_{0t} \ell_{jt} + \alpha_{0t} \cdot d_{jt}) + v_0 \cdot k_{j,-1}, $$ (eq:hh_budget) -where $b_{jt} = U_b^j z_t$ is household $j$'s preference shock, $d_{jt} = U_d^j z_t$ is household $j$'s endowment stream, $h_{j,-1}$ and $k_{j,-1}$ are given initial household stocks, and $\ell_{jt}$ is household labor supply. +where $b_{jt} = U_b^j z_t$ is household $j$'s preference shock, $d_{jt} = U_d^j z_t$ is household $j$'s endowment stream, $h_{j,-1}$ and $k_{j,-1}$ are given initial capital stocks, and $\ell_{jt}$ is household labor supply. + +Here $\mathbb{E}_0$ is expectation conditional on time-zero information $\mathcal{J}_0$. + The prices are: - $p_{0t}$: the time-0 Arrow-Debreu price vector for date-$t$ consumption goods (across states), so $p_{0t} \cdot c_{jt}$ is the time-0 value of date-$t$ consumption @@ -311,6 +363,8 @@ The prices are: These prices are determined in equilibrium. +In the DLE implementation below, these time-0 prices are recovered from the aggregate solution and encoded in the pricing objects `M_c`, `M_d`, `M_g`, and `M_k`. + This specification confines heterogeneity among consumers to four sources: 1. Differences in the preference processes $\{b_{jt}\}$, represented by different selections of $U_b^j$. @@ -323,40 +377,17 @@ This specification confines heterogeneity among consumers to four sources: The matrices $\Lambda, \Pi_h, \Delta_h, \Theta_h$ do *not* depend on $j$. -Because the technology matrices are common across households, every household's demand system has the same functional form. The four sources of heterogeneity listed above determine each household's effective wealth, which in turn determines the Lagrange multiplier $\mu_j$ on the budget constraint. - -This multiplier appears as the household's share of aggregate consumption in the sharing rule {eq}`eq:sharing_rule`. +Because the technology matrices are common across households, every household's demand system has the same functional form. -All households observe the same aggregate information $\mathcal{J}_t = [w_t, x_0]$. +All households observe the same aggregate information set $\{\mathcal{J}_t\}$; in our Markov setting we can take $\mathcal{J}_t$ to be generated by the current aggregate state $x_t$. These restrictions enable Gorman aggregation by ensuring that household demands are affine in wealth. -#### From individual problems to the aggregate problem - -Under Gorman's restrictions, the competitive equilibrium allocation solves a social planner's problem with Pareto weights $\mu_j = \mu_{0j}^w / \mu_{0a}^w$. - -Equilibrium prices emerge as the shadow prices from this problem. - -Next, we formulate the aggregate planning problem. +This will allow us to solve for aggregate allocations and prices without knowing the distribution of wealth across households as we shall see in {ref}`sharing_rules`. ### The aggregate planning problem -Summing the household budget constraints across $j = 1,\ldots,J$ gives - -$$ -\mathbb{E}_0 \sum_{t=0}^\infty \beta^t p_{0t} \cdot \left(\sum_j c_{jt}\right) -= \mathbb{E}_0 \sum_{t=0}^\infty \beta^t \left(w_{0t} \sum_j \ell_{jt} + p_{0t} \cdot \sum_j d_{jt}\right) + v_0 \cdot \sum_j k_{j,-1}. -$$ (eq:agg_budget_sum) - -In equilibrium, aggregate consumption $c_t = \sum_j c_{jt}$ must satisfy the economy's resource constraint - -$$ -\Phi_c c_t + \Phi_g g_t + \Phi_i i_t = \Gamma k_{t-1} + d_t, -$$ (eq:agg_resource_constraint) - -where aggregate endowment is $d_t = \sum_j d_{jt} = U_d z_t$ with $U_d = \sum_j U_d^j$. - -We construct the representative consumer's preferences by summing +We construct aggregates by summing across households: $$ h_{-1} = \sum_j h_{j,-1}, \quad @@ -365,16 +396,16 @@ U_b = \sum_j U_b^j, \quad U_d = \sum_j U_d^j. $$ (eq:agg_preference_aggregates) -With these aggregates, the planner maximizes +Aggregates are economy-wide totals: $c_t := \sum_j c_{jt}$, $b_t := \sum_j b_{jt}$, $d_t := \sum_j d_{jt}$, and similarly for $(i_t, k_t, h_t, s_t, g_t)$. + +The planner maximizes $$ -\frac{1}{2} \mathbb{E}_0 \sum_{t=0}^\infty \beta^t \left[(s_t - b_t)^\top(s_t - b_t) + g_t^\top g_t\right] $$ (eq:planner_objective) -where $g_t = \sum_j \ell_{jt}$ is the aggregate "intermediate good" that represents total labor supply in the DLE formulation. - -subject to technology constraints +where $g_t = \sum_j \ell_{jt}$ is aggregate labor supply, subject to technology constraints $$ \begin{aligned} @@ -393,41 +424,89 @@ b_t = U_b z_t, \quad d_t = U_d z_t. $$ (eq:exogenous_process) -Solving this aggregate planning problem yields aggregate allocations $(c_t, i_t, k_t, h_t, s_t, g_t)$ as functions of the aggregate state $x_t$. +Note that the constraints involve lagged stocks $(h_{t-1}, k_{t-1})$ and the current exogenous state $z_t$. These predetermined variables form the planner's state: -The first-order conditions give shadow prices $(M^c_t, M^k_t, M^h_t, M^s_t)$ associated with each constraint. +$$ +x_t = [h_{t-1}^\top, k_{t-1}^\top, z_t^\top]^\top. +$$ + +The solution expresses each aggregate quantity as a linear function of the state via selection matrices: + +$$ +q_t = S_q x_t \quad \text{for } q \in \{c, i, k, h, s, g, b, d\}. +$$ + +Similarly, shadow prices are linear in $x_t$: + +$$ +M_q x_t \quad \text{for } q \in \{c, k, h, s\}. +$$ These shadow prices correspond to competitive equilibrium prices. -## Household allocations +(sharing_rules)= +### Consumption sharing rules -A direct way to compute allocations to individuals would be to solve the problem each household faces in competitive equilibrium at equilibrium prices. +This section presents the Gorman consumption sharing rules that's in the headline of this lecture. -For a fixed Lagrange multiplier $\mu_{0j}^w$ on the household's budget constraint, the household's problem can be expressed as an optimal linear regulator with a state vector augmented to reflect aggregate state variables determining scaled Arrow-Debreu prices. +Our preference specification is an infinite-dimensional generalization of the static Gorman setup described above, where goods are indexed by both dates and states of the world. -One could compute individual allocations by iterating to find the multiplier $\mu_{0j}^w$ that satisfies the budget constraint. +Let $\mu_{0j}^w$ denote household $j$'s *time-zero marginal utility of wealth*, the Lagrange multiplier on its intertemporal budget constraint. -But there is a more elegant approach using Gorman's allocation rules. +Define the aggregate multiplier as + +$$ +\mu_{0a}^w := \sum_{j=1}^J \mu_{0j}^w. +$$ -### Allocation rules +The ratio $\mu_{0j}^w / \mu_{0a}^w$ is time-invariant and depends only on information available at time zero. -The allocation rule for household $j$'s labor (the "intermediate good" $g_{jt} = \ell_{jt}$) is +For notational convenience, we write $$ -\ell_{jt} = \mu_j \, \ell_t, -$$ (eq:labor_allocation) +\mu_j := \frac{\mu_{0j}^w}{\mu_{0a}^w}, +$$ + +so that $\sum_j \mu_j = 1$. -where $\ell_t = \sum_i \ell_{it}$ is aggregate labor and $\mu_j = \mu_{0j}^w / \mu_{0a}^w$ is the Gorman weight. +This is household $j$'s **Gorman weight**. + +The allocation rule for consumption has the form + +$$ +c_{jt} - \chi_{jt} = \mu_j (c_t - \chi_t), +$$ (eq:sharing_rule_baseline) + +or equivalently, + +$$ +c_{jt} = \mu_j c_t + \tilde{\chi}_{jt}, \qquad \tilde{\chi}_{jt} := \chi_{jt} - \mu_j \chi_t. +$$ (eq:sharing_rule) + +Here $\mu_j c_t$ is household $j$'s proportional share of aggregate consumption and $\tilde{\chi}_{jt}$ is the *deviation* from this proportional share. + +These deviations sum to zero: $\sum_j \tilde{\chi}_{jt} = 0$. + +The baseline term $\chi_{jt}$ is determined by the preference shock process $\{b_{jt}\}$ and initial household capital $h_{j,-1}$. + +We write the aggregate baseline as $\chi_t := \sum_{j=1}^J \chi_{jt}$. + + +The beauty of this representation is that the weight $\mu_j$ is time-invariant. + +Nonseparabilities in preferences (over time or across goods) affect only the construction of the baseline process $\{\chi_{jt}\}$ and the calculation of the weight $\mu_j$ implied by the distribution of wealth. + +#### Service allocation rule The first-order conditions for consumption services give $$ -s_{jt} - b_{jt} = \mu_{0j}^w \rho_t^0, +s_{jt} - b_{jt} = \mu_{0j}^w \rho_{0t}, $$ (eq:foc_services) -where $\rho_t^0$ is the shadow price of services from the aggregate problem. +where $\rho_{0t}$ is the time-0 shadow price for date-$t$ services from the aggregate problem. -Since the aggregate first-order condition is $s_t - b_t = \mu_{0a}^w \rho_t^0$, we can eliminate the price $\rho_t^0$ to obtain the service allocation rule in deviation form: +Since the aggregate first-order condition is $s_t - b_t = \mu_{0a}^w \rho_{0t}$, we can eliminate $\rho_{0t}$ to obtain: $$ s_{jt} - b_{jt} = \mu_j (s_t - b_t), @@ -435,30 +514,104 @@ $$ (eq:service_allocation) or equivalently $\tilde{s}_{jt} = \tilde{b}_{jt}$, where $\tilde{y}_{jt} := y_{jt} - \mu_j y_t$ for any variable $y$. -The beauty of this representation is that it does not involve prices directly. +This representation does not involve prices directly. + +#### Inverse canonical representation + +Given $\rho_{0t}$ from the aggregate solution, we can solve {eq}`eq:foc_services` for $s_{jt}$, then use the inverse canonical representation to compute $c_{jt}$ and $h_{jt}$: + +$$ +\begin{aligned} +c_{jt} &= -\Pi_h^{-1} \Lambda h_{j,t-1} + \Pi_h^{-1} s_{jt}, \\ +h_{jt} &= (\Delta_h - \Theta_h \Pi_h^{-1} \Lambda) h_{j,t-1} + \Theta_h \Pi_h^{-1} s_{jt}, +\end{aligned} +$$ (eq:inverse_canonical_full) + +with $h_{j,-1}$ given. + +This recursion requires a canonical household technology with square, invertible $\Pi_h$. + +#### Deviation form + +Define deviation processes $\tilde{y}_{jt} := y_{jt} - \mu_j y_t$ for any variable $y$. + +In particular, the deviation durable stock is $\tilde{h}_{jt} := h_{jt} - \mu_j h_t$. + +The deviation form of the inverse canonical representation {eq}`eq:inverse_canonical_full` is + +$$ +\begin{aligned} +\tilde{c}_{jt} &= -\Pi_h^{-1} \Lambda \tilde{h}_{j,t-1} + \Pi_h^{-1} \tilde{s}_{jt}, \\ +\tilde{h}_{jt} &= (\Delta_h - \Theta_h \Pi_h^{-1} \Lambda) \tilde{h}_{j,t-1} + \Theta_h \Pi_h^{-1} \tilde{s}_{jt}, +\end{aligned} +$$ (eq:inverse_canonical_dev) -### Deviation consumption +with $\tilde{h}_{j,-1} = h_{j,-1} - \mu_j h_{-1}$ given. -Given the sharing rule {eq}`eq:sharing_rule` introduced above, we now solve for household $j$'s deviation term $\tilde{\chi}_{jt}$ as a function of its deviation state $\tilde{\eta}_{jt} = h_{j,t-1} - \mu_j h_{t-1}$. +Associated with $\tilde{s}_{jt}$ is a synthetic consumption process $\tilde{\chi}_{jt}$ such that $\tilde{c}_{jt} = \tilde{\chi}_{jt}$ is the optimal sharing rule. -The "deviation" consumption $\tilde{\chi}_{jt}$ satisfies the inverse canonical representation +To construct $\tilde{\chi}_{jt}$, we substitute $\tilde{s}_{jt} = \tilde{b}_{jt}$ into the inverse canonical representation: $$ \begin{aligned} \tilde{\chi}_{jt} &= -\Pi_h^{-1} \Lambda \tilde{\eta}_{j,t-1} + \Pi_h^{-1} \tilde{b}_{jt}, \\ \tilde{\eta}_{jt} &= (\Delta_h - \Theta_h \Pi_h^{-1} \Lambda) \tilde{\eta}_{j,t-1} + \Theta_h \Pi_h^{-1} \tilde{b}_{jt}, \end{aligned} +$$ (eq:inverse_canonical) + +with $\tilde{\eta}_{j,-1} = \tilde{h}_{j,-1}$. + +Since $\tilde{s}_{jt} = \tilde{b}_{jt}$ and $\tilde{\eta}_{j,-1} = \tilde{h}_{j,-1}$, it follows from {eq}`eq:inverse_canonical_dev` and {eq}`eq:inverse_canonical` that $\tilde{c}_{jt} = \tilde{\chi}_{jt}$. + +Since the allocation rule for consumption can be expressed as + $$ +c_{jt} = \mu_j c_t + \tilde{\chi}_{jt}, +$$ (eq:cjt_recursion) + +we can append the recursion for $c_t$ from the aggregate problem to obtain a recursion generating $c_{jt}$. -where $\tilde{b}_{jt} = b_{jt} - \mu_j b_t$ is the deviation of household $j$'s bliss point from its share of the aggregate, and $\tilde{\eta}_{j,-1} = h_{j,-1} - \mu_j h_{-1}$. +#### Labor allocation rule -This recursion comes from inverting the household service technology. +The allocation rule for household $j$'s labor (the "intermediate good" $g_{jt} = \ell_{jt}$) is + +$$ +\ell_{jt} = \mu_j \, g_t, +$$ (eq:labor_allocation) + +where $g_t = \sum_j \ell_{jt}$ is aggregate labor. + +#### Risk sharing + +Because the coefficient $\mu_j$ is invariant over time and across goods, allocation rule {eq}`eq:sharing_rule_baseline` implies a form of risk pooling in the deviation process $\{c_{jt} - \chi_{jt}\}$. + +In the special case where the preference shock processes $\{b_{jt}\}$ are deterministic (in the sense that they reside in the information set $\mathcal{J}_0$), individual consumption goods will be perfectly correlated with their aggregate counterparts. + +## Household allocations + +A direct way to compute allocations to individuals would be to solve the problem each household faces in competitive equilibrium at equilibrium prices. + +For a fixed Lagrange multiplier $\mu_{0j}^w$ on the household's budget constraint, the household's problem can be expressed as an optimal linear regulator with a state vector augmented to reflect aggregate state variables determining scaled Arrow-Debreu prices. + +One could compute individual allocations by iterating to find the multiplier $\mu_{0j}^w$ that satisfies the budget constraint. + +But there is a more elegant approach using Gorman's allocation rules. + +### Computing household allocations + +We apply the consumption sharing rules {eq}`eq:sharing_rule` and {eq}`eq:sharing_rule_baseline` developed above in two steps. + +#### Compute deviation consumption + +Using the inverse canonical representation {eq}`eq:inverse_canonical`, we compute the deviation consumption process $\{\tilde{\chi}_{jt}\}$ from household $j$'s deviation bliss points $\{\tilde{b}_{jt}\}$ and initial deviation state $\tilde{\eta}_{j,-1} = h_{j,-1} - \mu_j h_{-1}$. When preferences include durables or habits ($\Lambda \neq 0$), the deviation consumption depends on the lagged deviation state $\tilde{\eta}_{j,t-1}$. The code solves this as a linear-quadratic control problem using a scaling trick: multiplying the transition matrices by $\sqrt{\beta}$ converts the discounted problem into an undiscounted one that can be solved with a standard discrete algebraic Riccati equation. -Step two is to compute the Gorman weight $\mu_j$ using household $j$'s budget constraint. +#### Compute the Gorman weight + +We compute the Gorman weight $\mu_j$ using household $j$'s budget constraint. We compute five present values: @@ -468,17 +621,39 @@ We compute five present values: - $W_{c2}$: present value of the deviation consumption stream $\{\tilde{\chi}_{jt}\}$ - $W_g$: present value of the intermediate good stream $\{g_t\}$ +Formally (suppressing state indices inside the dot products), + +$$ +\begin{aligned} +W_d &= \mathbb{E}_0 \sum_{t=0}^\infty \beta^t \, \alpha_{0t} \cdot d_{jt}, \\ +W_k &= v_0 \cdot k_{j,-1}, \\ +W_{c1} &= \mathbb{E}_0 \sum_{t=0}^\infty \beta^t \, p_{0t} \cdot c_t, \\ +W_{c2} &= \mathbb{E}_0 \sum_{t=0}^\infty \beta^t \, p_{0t} \cdot \tilde{\chi}_{jt}, \\ +W_g &= \mathbb{E}_0 \sum_{t=0}^\infty \beta^t \, w_{0t} \, g_t. +\end{aligned} +$$ + Each present value is computed using `doublej2` below to sum infinite series. -The budget constraint requires that the present value of consumption equals wealth. +The budget constraint {eq}`eq:hh_budget` requires that the present value of consumption equals wealth: -Substituting $c_{jt} = \mu_j c_t + \tilde{\chi}_{jt}$ gives +$$ +\underbrace{\mathbb{E}_0 \sum_{t=0}^\infty \beta^t p_{0t} \cdot c_{jt}}_{\text{consumption expenditure}} = \underbrace{v_0 \cdot k_{j,-1}}_{W_k} + \underbrace{\mathbb{E}_0 \sum_{t=0}^\infty \beta^t \alpha_{0t} \cdot d_{jt}}_{W_d} + \underbrace{\mathbb{E}_0 \sum_{t=0}^\infty \beta^t w_{0t} \ell_{jt}}_{\text{labor income}}. +$$ + +From the Gorman allocation rule $\ell_{jt} = \mu_j g_t$, the present value of labor income is $$ -\mu_j W_{c1} + W_{c2} = W_k + W_d + \mu_j W_g, +\mathbb{E}_0 \sum_{t=0}^\infty \beta^t w_{0t} \ell_{jt} = \mu_j \underbrace{\mathbb{E}_0 \sum_{t=0}^\infty \beta^t w_{0t} g_t}_{W_g} = \mu_j W_g. $$ -where $\mu_j W_g$ is household $j$'s share of intermediate good value. Solving for $\mu_j$: +Substituting the sharing rule $c_{jt} = \mu_j c_t + \tilde{\chi}_{jt}$ into the left side gives + +$$ +\mu_j W_{c1} + W_{c2} = W_k + W_d + \mu_j W_g. +$$ + +Solving for $\mu_j$: $$ \mu_j = \frac{W_k + W_d - W_{c2}}{W_{c1} - W_g}. @@ -693,6 +868,18 @@ def heter( This section implements a key result: the Arrow-Debreu allocation can be replicated using only a mutual fund and a one-period bond, rather than a complete set of contingent claims. +To match the implementation result in Chapter 12.6 of {cite:t}`HansenSargent2013`, we specialize to the one-good, constant-return case + +$$ +c_t + i_t = \gamma_1 k_{t-1} + d_t, \qquad +k_t = \delta_k k_{t-1} + i_t, \qquad +\beta = \frac{1}{\gamma_1 + \delta_k}. +$$ + +In this case the one-period riskless gross return is constant, $R = \gamma_1 + \delta_k = 1/\beta$. + +We also impose the Chapter 12.6 measurability restriction that the deviation baseline $\{\tilde{\chi}_{jt}\}$ is $\mathcal{J}_0$-measurable (for example, the preference-shock processes $\{b_{jt}\}$ are deterministic). + ### The trading mechanism Consider again an economy where each household $j$ initially owns claims to its own endowment stream $\{d_{jt}\}$. @@ -733,25 +920,27 @@ $$ which is the aggregate endowment. -If household $j$ holds fraction $\theta_j$ of this fund, it receives $\theta_j d_t$ in dividends. - -The proportional part of consumption $\mu_j c_t$ must be financed by the mutual fund and capital holdings. +If household $j$ holds fraction $\mu_j$ of this fund, it receives $\mu_j d_t$ in dividends. -Using our calibration ($\Phi_c = \Phi_i = \theta_k = 1$, $\Gamma = \gamma_1$), the resource constraint becomes $c_t + i_t = \gamma_1 k_{t-1} + d_t$ and capital accumulation is $k_t = \delta_k k_{t-1} + i_t$. +The proportional part of consumption $\mu_j c_t$ must be financed by the mutual fund and by holding a proportional share of aggregate capital. -Substituting $i_t = k_t - \delta_k k_{t-1}$ into the resource constraint gives: +Combining the resource constraint and capital accumulation gives $$ -c_t + k_t = (\delta_k + \gamma_1) k_{t-1} + d_t. +c_t + k_t = R k_{t-1} + d_t. $$ -Holding $\theta_j$ shares of aggregate wealth (capital plus claims to endowments) delivers $\theta_j [(\delta_k + \gamma_1)k_{t-1} + d_t] = \theta_j (c_t + k_t)$. +Multiplying by $\mu_j$ yields -For this to finance $\mu_j c_t$ plus reinvestment $\mu_j k_t$, we need $\theta_j = \mu_j$. +$$ +\mu_j c_t + \mu_j k_t = R (\mu_j k_{t-1}) + \mu_j d_t, +$$ -The remaining term $\tilde{\chi}_{jt}$ is financed by adjusting the bond position according to the recursion derived below. +so mutual-fund dividends $\mu_j d_t$ and the return on a proportional capital position $\mu_j k_{t-1}$ finance household $j$'s proportional claim $\mu_j c_t$ plus the reinvestment needed to maintain its share $\mu_j k_t$. -Setting $\theta_j = \mu_j$ thus ensures that the mutual fund finances exactly the proportional share $\mu_j c_t$, while the bond handles the deviation $\tilde{\chi}_{jt}$. +The remaining term $\tilde{\chi}_{jt}$ is financed by adjusting an *additional* bond position $\hat{k}_{jt}$ according to the recursion derived below. + +Holding $\mu_j$ shares of the mutual fund thus finances exactly the proportional component $\mu_j c_t$ (together with rolling over the proportional capital position), while the bond handles the deviation $\tilde{\chi}_{jt}$. This transforms heterogeneous endowment risk into proportional shares of aggregate risk. @@ -760,34 +949,16 @@ We now derive the law of motion for the bond position $\hat{k}_{jt}$. First, write the budget constraint. Household $j$'s time-$t$ resources equal uses: $$ -\underbrace{\mu_j [(\delta_k + \gamma_1) k_{t-1} + d_t]}_{\text{mutual fund income}} + \underbrace{R \hat{k}_{j,t-1}}_{\text{bond return}} = \underbrace{c_{jt}}_{\text{consumption}} + \underbrace{\mu_j k_t}_{\text{new fund shares}} + \underbrace{\hat{k}_{jt}}_{\text{new bonds}} +\underbrace{\mu_j d_t}_{\text{mutual fund dividend}} + \underbrace{R(\mu_j k_{t-1} + \hat{k}_{j,t-1})}_{\text{bond return}} = \underbrace{c_{jt}}_{\text{consumption}} + \underbrace{(\mu_j k_t + \hat{k}_{jt})}_{\text{new bonds}} $$ -where $R := \delta_k + \gamma_1$ is the gross return. - -```{note} -The constant gross return $R = \delta_k + \gamma_1$ arises from our specific calibration ($\Phi_c = \Phi_i = \Theta_k = 1$, $\Gamma = \gamma_1$). -``` - Substituting the sharing rule by replacing $c_{jt}$ with $\mu_j c_t + \tilde{\chi}_{jt}$ gives: $$ -\mu_j [(\delta_k + \gamma_1) k_{t-1} + d_t] + R \hat{k}_{j,t-1} = \mu_j c_t + \tilde{\chi}_{jt} + \mu_j k_t + \hat{k}_{jt} -$$ - -The aggregate economy satisfies $c_t + k_t = (\delta_k + \gamma_1) k_{t-1} + d_t$, so: - -$$ -(\delta_k + \gamma_1) k_{t-1} + d_t = c_t + k_t -$$ - -Substituting into the left-hand side: - -$$ -\mu_j (c_t + k_t) + R \hat{k}_{j,t-1} = \mu_j c_t + \tilde{\chi}_{jt} + \mu_j k_t + \hat{k}_{jt} +\mu_j d_t + R(\mu_j k_{t-1} + \hat{k}_{j,t-1}) = \mu_j c_t + \tilde{\chi}_{jt} + (\mu_j k_t + \hat{k}_{jt}) $$ -The $\mu_j c_t$ and $\mu_j k_t$ terms cancel: +Using $\mu_j c_t + \mu_j k_t = R(\mu_j k_{t-1}) + \mu_j d_t$, the proportional terms cancel and we obtain $$ R \hat{k}_{j,t-1} = \tilde{\chi}_{jt} + \hat{k}_{jt} @@ -836,14 +1007,14 @@ $$ Solving for the initial position gives: $$ -\hat{k}_{j0} = \sum_{t=1}^\infty R^{-t} \tilde{\chi}_{jt}. +\hat{k}_{j0} = \sum_{t=1}^\infty R^{-t} \tilde{\chi}_{jt} = \sum_{t=1}^\infty \beta^t \tilde{\chi}_{jt}. $$ This is the present value of future deviation consumption. -Since $\tilde{\chi}_{jt}$ depends only on the deterministic deviation between household $j$'s preference shocks and its share of aggregate preference shocks, this sum is in the time-zero information set and can be computed at date zero. +Under the Chapter 12.6 measurability restriction ($\tilde{\chi}_{jt} \in \mathcal{J}_0$), this sum is known at date zero and can be used to set the initial bond position. -```{note} Connection to Rubinstein's two-fund theorem +```{note} This construction displays a multiperiod counterpart to an aggregation result for security markets derived by {cite:t}`rubinstein1974aggregation`. @@ -1048,7 +1219,7 @@ We reproduce the two-household example from Chapter 12.6 of {cite:t}`HansenSarge There are two households, each with preferences $$ --\frac{1}{2} \mathbb{E} \sum_{t=0}^{\infty} \beta^t \left[(c^i_t - b^i_t)^2 + \ell^{i\,2}_t\right] \mid \mathcal{J}_0, \quad i = 1, 2. +-\frac{1}{2} \mathbb{E}_0 \sum_{t=0}^{\infty} \beta^t \left[(c^i_t - b^i_t)^2 + \ell^{i\,2}_t\right], \quad i = 1, 2. $$ We set $b^i_t = 15$ for $i = 1, 2$, so the aggregate preference shock is $b_t = \sum_i b^i_t = 30$. The endowment processes are @@ -1066,23 +1237,22 @@ $$ with $w^2_t$ Gaussian white noise with variance $(0.25)^2$. +Here $w^1_t$ and $w^2_t$ are components of the exogenous innovation vector driving $z_t$ and should not be confused with the wage-price sequence $w_{0t}$ in the household budget constraint. + ```{code-cell} ipython3 -ϕ_1 = 1e-5 -γ_1 = 0.1 -δ_k = 0.95 +# Technology: c + i = γ_1 * k_{t-1} + d, with β = 1/(γ_1 + δ_k) +# No habits/durables (Λ = δ_h = θ_h = 0), so services = consumption +ϕ_1, γ_1, δ_k = 1e-5, 0.1, 0.95 β = 1.0 / (γ_1 + δ_k) - -θ_k = 1.0 -δ_h = 0.0 -θ_h = 0.0 -Λ = 0.0 -Π_h = 1.0 +θ_k, δ_h, θ_h, Λ, Π_h = 1.0, 0.0, 0.0, 0.0, 1.0 Φ_c = np.array([[1.0], [0.0]]) Φ_g = np.array([[0.0], [1.0]]) Φ_i = np.array([[1.0], [-ϕ_1]]) Γ = np.array([[γ_1], [0.0]]) +# Exogenous state z_t = [1, d_2_t, d_2_{t-1}, ε_1_t, ε_2_t]' +# AR(2) aggregate shock in components 2-3; i.i.d. idiosyncratic in 4-5 A_22 = np.array([ [1.0, 0.0, 0.0, 0.0, 0.0], [0.0, 1.2, -0.22, 0.0, 0.0], @@ -1099,10 +1269,13 @@ C_2 = np.array([ [0.0, 1.0], ], dtype=float) +# Both households: constant bliss point b = 15 U_b1 = np.array([[15.0, 0.0, 0.0, 0.0, 0.0]]) U_b2 = np.array([[15.0, 0.0, 0.0, 0.0, 0.0]]) U_b = U_b1 + U_b2 +# HH1: d_1 = 4 + 0.2ε_2 (idiosyncratic); +# HH2: d_2 = 3 + d_tilde2 (aggregate AR(2)) U_d1 = np.array([[4.0, 0.0, 0.0, 0.2, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0]]) U_d2 = np.array([[3.0, 1.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0]]) U_d = U_d1 + U_d2 @@ -1160,20 +1333,6 @@ ax.legend() plt.show() ``` -### Risk sharing - -The Gorman sharing rule has strong implications for risk sharing across households. - -Because the coefficient $\mu_j = \mu_{0j}^w / \mu_{0a}^w$ is invariant over time and across goods, allocation rule {eq}`eq:sharing_rule` implies a form of risk pooling in the deviation process $\{c_{jt} - \chi_{jt}\}$. - -All households share aggregate consumption risk in fixed proportions determined at date zero. - -Non-separabilities in preferences (either over time or across goods) affect only the construction of the baseline process $\{\chi_{jt}\}$ and the calculation of the risk-sharing coefficient $\mu_j$ implied by the distribution of wealth. - -They do not break the proportional sharing of aggregate risk. - -In the special case where the preference shock processes $\{b_{jt}\}$ are deterministic in the sense that they reside in the information set $\mathcal{J}_0$, individual consumption goods will be perfectly correlated with their aggregate counterparts (conditioned on $\mathcal{J}_0$). - The next figure plots the limited-markets bond adjustment and confirms that the adjustments sum to approximately zero. ```{code-cell} ipython3 @@ -1209,50 +1368,84 @@ We now extend the two-household example to an economy with many households. The exogenous state vector is $$ -z_t = \begin{bmatrix} 1 \\ d_{a,t} \\ d_{a,t-1} \\ w_{2,t} \\ \vdots \\ w_{n,t} \\ w_{n+1,t} \\ \vdots \\ w_{2n,t} \end{bmatrix}, +z_t = +\begin{bmatrix} +1 \\ +d_{a,t} \\ +d_{a,t-1} \\ +\eta_{J_a+1,t} \\ +\vdots \\ +\eta_{J,t} \\ +\xi_{1,t} \\ +\vdots \\ +\xi_{J,t} +\end{bmatrix}, $$ -where the first three components track a constant and the "ghost" aggregate endowment process, and the remaining components are i.i.d. shocks for individual endowments ($w_{2,t}, \ldots, w_{n,t}$) and preference shocks ($w_{n+1,t}, \ldots, w_{2n,t}$). +where $J$ is the number of households and $J_a$ is the number of "absorbing" households (defined below). + +The first three components track a constant and the "ghost" aggregate endowment process. +The components $\{\eta_{j,t}\}_{j=J_a+1}^J$ are idiosyncratic endowment states for the non-absorbing households. +The components $\{\xi_{j,t}\}_{j=1}^J$ are idiosyncratic preference-shock states (often set to zero in experiments). The aggregate endowment follows an AR(2) process: $$ -d_{a,t} = \rho_1 d_{a,t-1} + \rho_2 d_{a,t-2} + \sigma_a w_{1,t}, +d_{a,t+1} = \rho_1 d_{a,t} + \rho_2 d_{a,t-1} + \sigma_a w_{1,t+1}, $$ -where we can set the $\rho_j$'s to capture persistent aggregate fluctuations. +where $w_{1,t+1}$ is the aggregate innovation (the first component of $w_{t+1}$) and we can set the $\rho_j$'s to capture persistent aggregate fluctuations. -Let $n_a$ denote the number of "absorbing" households. For households $j > n_a$, individual endowments are +Let $J_a$ denote the number of "absorbing" households. +For households $j > J_a$, individual endowments are $$ -d_{jt} = \alpha_j + \phi_j d_{a,t} + \sigma_j w_{j,t}, \quad j = n_a + 1, \ldots, n. +d_{jt} = \alpha_j + \phi_j d_{a,t} + \eta_{j,t}, \quad j = J_a + 1, \ldots, J. $$ -The first $n_a$ households absorb the negative of all idiosyncratic shocks to ensure aggregation: +The first $J_a$ households absorb the negative of all idiosyncratic endowment shocks to ensure aggregation: $$ -d_{jt} = \alpha_j + \phi_j d_{a,t} - \frac{1}{n_a} \sum_{k=n_a+1}^n \sigma_k w_{k,t}, \quad j = 1, \ldots, n_a. +d_{jt} = \alpha_j + \phi_j d_{a,t} - \frac{1}{J_a} \sum_{k=J_a+1}^J \eta_{k,t}, \quad j = 1, \ldots, J_a. $$ This construction ensures that $$ -\sum_{j=1}^n d_{j,t} = \sum_{j=1}^n \alpha_j + \left(\sum_{j=1}^n \phi_j\right) d_{a,t}. +\sum_{j=1}^J d_{j,t} = \sum_{j=1}^J \alpha_j + \left(\sum_{j=1}^J \phi_j\right) d_{a,t}. $$ -Imposing $\sum_{j=1}^n \phi_j = 1$ gives +Imposing $\sum_{j=1}^J \phi_j = 1$ gives $$ -\sum_{j=1}^n d_{j,t} = \sum_{j=1}^n \alpha_j + d_{a,t}. +\sum_{j=1}^J d_{j,t} = \sum_{j=1}^J \alpha_j + d_{a,t}. $$ Preference shocks are muted to simplify experiments: $$ -b_{jt} = \bar{b} + \gamma_j w_{n+j,t}, \quad j = 1, \ldots, n, +b_{jt} = \bar{b} + \xi_{j,t}, \quad j = 1, \ldots, J, +$$ + +where the $\xi_{j,t}$ processes are taken to be small (and can be shut off entirely by setting their innovation loadings to zero). + +To complete the specification, we let the idiosyncratic states follow AR(1) laws of motion +(all innovations are components of the i.i.d. vector $w_{t+1}$ in $z_{t+1} = A_{22} z_t + C_2 w_{t+1}$): + +$$ +\eta_{j,t+1} = \rho^{d}_j \eta_{j,t} + \sigma_j \varepsilon^{d}_{j,t+1}, +\qquad j = J_a + 1, \ldots, J, +$$ + +and + +$$ +\xi_{j,t+1} = \rho^{b}_j \xi_{j,t} + \gamma_j \varepsilon^{b}_{j,t+1}, +\qquad j = 1, \ldots, J. $$ -where the $\gamma_j$'s are small. +Here $\varepsilon^{d}_{j,t+1}$ and $\varepsilon^{b}_{j,t+1}$ are mean-zero, unit-variance innovations (components of $w_{t+1}$). +Setting $\rho^{d}_j = 0$ (or $\rho^{b}_j = 0$) recovers i.i.d. shocks. ```{code-cell} ipython3 def build_reverse_engineered_gorman_extended( @@ -1348,7 +1541,7 @@ def build_reverse_engineered_gorman_extended( # Ud_per_house: endowment loading # First n_absorb households: - # d_{jt} = alpha_j + phi_j * d_{a,t} - (1/n_absorb) * sum_{k>n_absorb} eta_{k,t} + # d_{jt} = alpha_j + phi_j * d_{a,t} - (1/n_ab) * sum_{k>n_ab} eta_{k,t} # Remaining households k > n_absorb: # d_{kt} = alpha_k + phi_k * d_{a,t} + eta_{k,t} Ud_per_house = [] @@ -1465,8 +1658,6 @@ A22, C2, Ub, Ud, Ub_list, Ud_list, x0 = build_reverse_engineered_gorman_extended n_absorb=n_absorb, ) -print(f"State dimension nz = {A22.shape[0]}") -print(f"Shock dimension nw = {C2.shape[1]}") print(f"sum(φs) = {np.sum(φs):.6f} (should be 1.0)") ``` @@ -1487,8 +1678,8 @@ paths, econ = solve_model(info_ar1, tech_ar1, pref_ar1, ```{code-cell} ipython3 print(f"State dimension: {A22.shape[0]}, shock dimension: {C2.shape[1]}") -print(f"Aggregate: ρ₁={ρ1:.3f}, ρ₂={ρ2:.3f}, σₐ={σ_a:.2f}") -print(f"Endowments: α ∈ [{np.min(αs):.2f}, {np.max(αs):.2f}], Σφ={np.sum(φs):.6f}") +print(f"Aggregate: ρ_1={ρ1:.3f}, ρ_2={ρ2:.3f}, σ_a={σ_a:.2f}") +print(f"Endowments: α in [{np.min(αs):.2f}, {np.max(αs):.2f}], Σφ={np.sum(φs):.6f}") ``` The next plots show household consumption and dividend paths after discarding the initial burn-in period. @@ -1625,7 +1816,7 @@ axes[0].set_ylabel('capital') axes[1].plot(da_irf, lw=2) axes[1].set_xlabel('time') -axes[1].set_ylabel(r'$d_{a,t}$ (aggregate endowment shock)') +axes[1].set_ylabel(r'$d_{a,t}$') plt.tight_layout() @@ -1699,12 +1890,10 @@ Consider redistributing consumption by choosing new weights $\{\lambda_j^*\}$ sa The new allocation $c_{jt} - \chi_{jt} = \lambda_j^* (c_t - \chi_t)$ preserves aggregate dynamics $(c_t, k_t)$ while reallocating consumption across households. -### Post-tax-and-transfer consumption and income - Let $R := \delta_k + \gamma_1$. For a given Pareto-weight vector $\omega$, -household $j$'s assets are $a_{j,t} \equiv \omega_j k_t + \hat{k}_{j,t}$ and income is +household $j$'s assets are $a_{j,t} := \omega_j k_t + \hat{k}_{j,t}$ and income is $$ y_{j,t}(\omega) = \omega_j d_t + (R-1) a_{j,t-1}. @@ -1926,10 +2115,6 @@ y_post_pct = _pct(y_post[:, :T_ts]) c_pre_pct = _pct(c_pre[:, :T_ts]) c_post_pct = _pct(c_post[:, :T_ts]) -print(f"\nRedistribution effects:") -print(f" income dispersion: {np.std(y_pre):.3f} → {np.std(y_post):.3f}") -print(f" consumption dispersion: {np.std(c_pre):.3f} → {np.std(c_post):.3f}") - fig, ax = plt.subplots(1, 3, figsize=(14, 4), sharey=True) ax[0].plot(t, y_pre_pct[0], label="p90", lw=2) @@ -1956,3 +2141,9 @@ ax[2].legend() plt.tight_layout() plt.show() ``` + +The figures shows a clear reduction in income and consumption inequality after redistribution via Pareto weights. + +We also see how insurance smooths consumption relative to income, especially for lower-percentile households. + +The post-tax-and-transfer income path exhibits less volatility for the lower percentiles compared to the pre-tax-and-transfer income. From bfc0de81912dee7a28339abe11ede3dfb162bafc Mon Sep 17 00:00:00 2001 From: Humphrey Yang Date: Fri, 26 Dec 2025 15:54:50 +1100 Subject: [PATCH 09/17] updates --- lectures/gorman_heterogeneous_households.md | 107 ++++++++++++-------- 1 file changed, 63 insertions(+), 44 deletions(-) diff --git a/lectures/gorman_heterogeneous_households.md b/lectures/gorman_heterogeneous_households.md index 7ecfade4..66e0cbc9 100644 --- a/lectures/gorman_heterogeneous_households.md +++ b/lectures/gorman_heterogeneous_households.md @@ -53,7 +53,7 @@ import matplotlib.pyplot as plt Gorman aggregation lets us solve heterogeneous-household economies in two steps: solve a representative-agent linear-quadratic planning problem for aggregates, then recover household allocations via a "sharing rule" with household-specific deviation terms. -Gorman conditions ensure all consumers have parallel Engel curves, so aggregate allocations and prices can be determined independently of distribution. +The key is that Gorman conditions ensure all consumers have parallel Engel curves, so aggregate allocations and prices can be determined independently of distribution. This eliminates the standard problem where the utility possibility frontier shifts with endowment changes, making it impossible to rank allocations without specifying distributional weights. @@ -254,6 +254,8 @@ In the dynamic setting, we set the utility index $u^j$ equal to household $j$'s The ratio $\mu_{0j}^w / \mu_{0a}^w$ (where $\mu_{0a}^w = \sum_j \mu_{0j}^w$) then serves as the time-invariant Gorman weight that determines household $j$'s share of aggregate consumption in excess of baseline. +Without spoiling further, we now introduce the basic setup for the dynamic heterogeneous-household economy. + ## Set up @@ -290,6 +292,8 @@ The vector $z_t$ typically contains three types of components. The selection matrices $U_b$ and $U_d$ pick out which components of $z_t$ affect household preferences (bliss points) and endowments. +This structure allows us to model both aggregate and idiosyncratic shocks within a unified framework at a later stage. + ### Technologies The economy's resource constraint is @@ -946,7 +950,11 @@ This transforms heterogeneous endowment risk into proportional shares of aggrega We now derive the law of motion for the bond position $\hat{k}_{jt}$. -First, write the budget constraint. Household $j$'s time-$t$ resources equal uses: +First, write the budget constraint. + +Household $j$'s time-$t$ resources are its mutual fund dividend $\mu_j d_t$ plus the return on its bond position $R(\mu_j k_{t-1} + \hat{k}_{j,t-1})$. + +Its uses of funds are consumption $c_{jt}$ plus the new bond position $(\mu_j k_t + \hat{k}_{jt})$: $$ \underbrace{\mu_j d_t}_{\text{mutual fund dividend}} + \underbrace{R(\mu_j k_{t-1} + \hat{k}_{j,t-1})}_{\text{bond return}} = \underbrace{c_{jt}}_{\text{consumption}} + \underbrace{(\mu_j k_t + \hat{k}_{jt})}_{\text{new bonds}} @@ -1012,7 +1020,7 @@ $$ This is the present value of future deviation consumption. -Under the Chapter 12.6 measurability restriction ($\tilde{\chi}_{jt} \in \mathcal{J}_0$), this sum is known at date zero and can be used to set the initial bond position. +Under the measurability restriction ($\tilde{\chi}_{jt} \in \mathcal{J}_0$), this sum is known at date zero and can be used to set the initial bond position. ```{note} @@ -1030,7 +1038,9 @@ All changes over time in portfolio composition take place through transactions i Below is the code that computes household allocations and limited-markets portfolios along a fixed aggregate path according to the mechanism described above ```{code-cell} ipython3 -def compute_household_paths(econ, U_b_list, U_d_list, x0, x_path, γ_1, Λ, h0i=None, k0i=None): +def compute_household_paths( + econ, U_b_list, U_d_list, x0, x_path, + γ_1, Λ, h0i=None, k0i=None): """ Compute household allocations and limited-markets portfolios along a fixed aggregate path. @@ -1131,7 +1141,8 @@ def compute_household_paths(econ, U_b_list, U_d_list, x0, x_path, γ_1, Λ, h0i= # At t=0, assume η_{-1} = 0 χ_tilde[j, 0] = (Π_inv @ b_tilde[:, 0]).squeeze() for t in range(1, T): - χ_tilde[j, t] = (-Π_inv @ Λ @ η[:, t - 1] + Π_inv @ b_tilde[:, t]).squeeze() + χ_tilde[j, t] = ( + -Π_inv @ Λ @ η[:, t - 1] + Π_inv @ b_tilde[:, t]).squeeze() η[:, t] = (A_h @ η[:, t - 1] + B_h @ b_tilde[:, t]).squeeze() c_j[j] = (μ[j] * c[0] + χ_tilde[j]).squeeze() @@ -1179,7 +1190,15 @@ def compute_household_paths(econ, U_b_list, U_d_list, x0, x_path, γ_1, Λ, h0i= ``` The next function collects everything to solve the planner's problem and compute household paths -into one function +into one function. + +We first use the `DLE` class to set up the representative-agent DLE problem. + +Then we build the full initial state by stacking zeros for lagged durables and capital with the initial exogenous state. + +Using the `LQ` class, we solve the LQ problem and simulate paths for the full state. + +Finally, we call `compute_household_paths` to get household allocations and limited-markets portfolios along the simulated path ```{code-cell} ipython3 def solve_model(info, tech, pref, U_b_list, U_d_list, γ_1, Λ, z0, ts_length=2000): @@ -1462,14 +1481,16 @@ def build_reverse_engineered_gorman_extended( allowing the full heterogeneous dynamics to be captured. The state vector is: - z_t = [1, d_{a,t}, d_{a,t-1}, eta_{n_absorb+1,t}, ..., eta_{n,t}, xi_{1,t}, ..., xi_{n,t}] + z_t = [1, d_{a,t}, d_{a,t-1}, eta_{n_absorb+1,t}, + ..., eta_{n,t}, xi_{1,t}, ..., xi_{n,t}] - The first n_absorb households absorb the negative sum of all idiosyncratic shocks - to ensure shocks sum to zero: - sum_{j=1}^{n_absorb} (-1/n_absorb * sum_{k>n_absorb} eta_k) + sum_{k>n_absorb} eta_k = 0 + The first n_absorb households absorb the negative sum + of all idiosyncratic shocks to ensure shocks sum to zero: + sum_{j=1}^{n_absorb} (-1/n_absorb * sum_{k>n_absorb} eta_k) + + sum_{k>n_absorb} eta_k = 0 - Each household k > n_absorb has its own idiosyncratic endowment shock eta_{k,t} - following an AR(1): + Each household k > n_absorb has its own idiosyncratic endowment shock + eta_{k,t} following an AR(1): eta_{k,t+1} = rho_idio[k] * eta_{k,t} + sigma_k * w_{k,t+1} @@ -1492,13 +1513,7 @@ def build_reverse_engineered_gorman_extended( if n_absorb < 1 or n_absorb >= n: raise ValueError(f"n_absorb must be in [1, n-1], got {n_absorb} with n={n}") - # State vector: - # z_t = [1, d_{a,t}, d_{a,t-1}, eta_{n_absorb+1,t}, ..., eta_{n,t}, xi_{1,t}, ..., xi_{n,t}] - # where eta_{j,t} are idiosyncratic endowment shocks (j=n_absorb+1..n) - # and xi_{j,t} are preference shocks (j=1..n) - # First n_absorb households absorb -1/n_absorb * sum(eta_j) to ensure shocks sum to zero - # Dimension: 3 + (n - n_absorb) + n = 2n + 3 - n_absorb - + # Dimensions n_idio = n - n_absorb # eta_{n_absorb+1}, ..., eta_n n_pref = n # xi_1, ..., xi_n nz = 3 + n_idio + n_pref @@ -1580,7 +1595,7 @@ def build_reverse_engineered_gorman_extended( ### 100-household reverse engineered economy -We now instantiate a 100-household economy using the reverse-engineered Gorman specification. +We now instantiate a 100-household economy using the setup above. We use the same technology and preference parameters as the two-household example. @@ -1679,7 +1694,7 @@ paths, econ = solve_model(info_ar1, tech_ar1, pref_ar1, ```{code-cell} ipython3 print(f"State dimension: {A22.shape[0]}, shock dimension: {C2.shape[1]}") print(f"Aggregate: ρ_1={ρ1:.3f}, ρ_2={ρ2:.3f}, σ_a={σ_a:.2f}") -print(f"Endowments: α in [{np.min(αs):.2f}, {np.max(αs):.2f}], Σφ={np.sum(φs):.6f}") +print(f"Endowments: α in [{np.min(αs):.2f}, {np.max(αs):.2f}]") ``` The next plots show household consumption and dividend paths after discarding the initial burn-in period. @@ -1705,7 +1720,9 @@ plt.show() ### Closed-loop state-space system -The DLE framework represents the economy as a linear state-space system. After solving the optimal control problem and substituting the policy rule, we obtain a closed-loop system: +The DLE framework represents the economy as a linear state-space system. + +After solving the optimal control problem and substituting the policy rule, we obtain a closed-loop system: $$ x_{t+1} = A_0 x_t + C w_{t+1}, @@ -1762,9 +1779,6 @@ G = np.vstack([ n_h = np.atleast_2d(econ.thetah).shape[0] n_k = np.atleast_2d(econ.thetak).shape[0] n_endo = n_h + n_k # endogenous state dimension - -print(f"Shapes: A0 {A0.shape}, C {C.shape}, G {G.shape}") -print(f"max |A0[{n_endo}:,{n_endo}:] - A22| = {np.max(np.abs(A0[n_endo:, n_endo:] - A22)):.2e}") ``` With the state space representation, we can compute impulse responses to show how shocks propagate through the economy. @@ -1834,7 +1848,6 @@ This causes capital to rise as $d_{a,t}$ falls — this is permanent income logi Next, we examine if the household consumption and endowment paths generated by the simulation obey the Gorman sharing rule ```{code-cell} ipython3 -# Now stack the household consumption panel with household endowments and rerun DMD. c_j_t0 = paths["c_j"][..., t0:] d_j_t0 = paths["d_share"][..., t0:] @@ -1854,15 +1867,16 @@ n_to_plot = 1 fig, axes = plt.subplots(2, 1, figsize=(14, 8)) # Top panel: Aggregate endowment -axes[0].plot(time_idx, d_agg[:T_plot], linewidth=2.5, color='C0', label='Aggregate endowment $d_t$') +axes[0].plot(time_idx, d_agg[:T_plot], linewidth=2.5, color='C0', + label='Aggregate endowment $d_t$') axes[0].set_ylabel('Endowment') -axes[0].set_title('Aggregate Endowment (contains ghost AR(2) process)') +axes[0].set_title('Aggregate Endowment') axes[0].legend() # Also plot the mean across households d_mean = d_households[:, :T_plot].mean(axis=0) axes[1].plot(time_idx, d_mean, linewidth=2.5, color='black', linestyle='--', - label=f'Mean across {d_households.shape[0]} households', alpha=0.8) + label=f'Mean across {d_households.shape[0]} households', alpha=0.8) axes[1].set_xlabel('Time (after burn-in)') axes[1].set_ylabel('Endowment') @@ -1912,7 +1926,9 @@ To implement a redistribution from $\mu$ to $\lambda^*$, we construct new Pareto 3. Leave middle-$j$ types relatively unaffected 4. Preserve the constraint $\sum_{j=1}^J \lambda_j^* = 1$ -We implement this using a smooth transformation. Let $\{\lambda_j\}_{j=1}^J$ denote the original competitive equilibrium Pareto weights (sorted in descending order). Define the redistribution function: +We implement this using a smooth transformation. + +Let $\{\lambda_j\}_{j=1}^J$ denote the original competitive equilibrium Pareto weights (sorted in descending order). Define the redistribution function: $$ f(j; J) = \frac{j-1}{J-1}, \qquad @@ -1922,8 +1938,8 @@ $$ where: - $j \in \{1, \ldots, J\}$ is the household index -- $\alpha > 0$ controls the overall magnitude of redistribution (with $\tau$ maximized at the extremes) -- $\beta > 1$ controls the progressivity (higher $\beta$ concentrates redistribution more strongly in the tails) +- $\alpha > 0$ controls the overall magnitude of redistribution +- $\beta$ controls the progressivity (higher $\beta$ concentrates redistribution more strongly in the tails) The redistributed Pareto weights are: @@ -1951,7 +1967,7 @@ def create_redistributed_weights(λ_orig, α=0.5, β=2.0): λ_bar = 1.0 / J j_indices = np.arange(J) f_j = j_indices / (J - 1) - dist_from_median = np.abs(f_j - 0.5) / 0.5 # in [0, 1], smallest near the median + dist_from_median = np.abs(f_j - 0.5) / 0.5 τ_j = np.clip(α * (dist_from_median ** β), 0.0, 1.0) λ_tilde = λ_orig + τ_j * (λ_bar - λ_orig) λ_star = λ_tilde / λ_tilde.sum() @@ -1977,10 +1993,12 @@ red_β = 0.0 fig, axes = plt.subplots(1, 2, figsize=(14, 5)) n_plot = len(λ_orig_sorted) -axes[0].plot(λ_orig_sorted[:n_plot], 'o-', label=r'original $\lambda_j$', alpha=0.7, lw=2) -axes[0].plot(λ_star_sorted[:n_plot], 's-', label=r'redistributed $\lambda_j^*$', alpha=0.7, lw=2) +axes[0].plot(λ_orig_sorted[:n_plot], 'o-', + label=r'original $\lambda_j$', alpha=0.7, lw=2) +axes[0].plot(λ_star_sorted[:n_plot], 's-', + label=r'redistributed $\lambda_j^*$', alpha=0.7, lw=2) axes[0].axhline(1.0 / len(λ_orig_sorted), color='k', linestyle='--', - label=f'equal weight (1/{len(λ_orig_sorted)})', alpha=0.5, lw=2) + label=f'equal weight (1/{len(λ_orig_sorted)})', alpha=0.5, lw=2) axes[0].set_xlabel(r'household index $j$ (sorted by $\lambda$)') axes[0].set_ylabel('Pareto weight') axes[0].legend() @@ -2050,7 +2068,8 @@ def allocation_from_weights(paths, econ, U_b_list, weights, γ_1, Λ, h0i=None): # At t=0, assume η_{-1} = 0 χ_tilde[j, 0] = (Π_inv @ b_tilde[:, 0]).squeeze() for t in range(1, T): - χ_tilde[j, t] = (-Π_inv @ Λ @ η[:, t - 1] + Π_inv @ b_tilde[:, t]).squeeze() + χ_tilde[j, t] = ( + -Π_inv @ Λ @ η[:, t - 1] + Π_inv @ b_tilde[:, t]).squeeze() η[:, t] = (A_h @ η[:, t - 1] + B_h @ b_tilde[:, t]).squeeze() c_j[j] = (weights[j] * c_agg[0] + χ_tilde[j]).squeeze() @@ -2071,7 +2090,9 @@ def allocation_from_weights(paths, econ, U_b_list, weights, γ_1, Λ, h0i=None): # Net income: dividend share + asset return y_net = weights[:, None] * d_agg[0, :] + (R - 1) * a_lag - return {"c": c_j, "y_net": y_net, "χ_tilde": χ_tilde, "k_hat": k_hat, "a_total": a_total} + return {"c": c_j, "y_net": y_net, + "χ_tilde": χ_tilde, "k_hat": k_hat, + "a_total": a_total} ``` ```{code-cell} ipython3 @@ -2090,14 +2111,12 @@ idx_sorted = np.argsort(-μ_values) λ_star = np.empty_like(μ_values, dtype=float) λ_star[idx_sorted] = λ_star_sorted -print(f"Weight redistribution:") -print(f" std(μ): {np.std(μ_values):.4f} → std(λ*): {np.std(λ_star):.4f}") -print(f" p90/p10: {np.percentile(μ_values, 90)/np.percentile(μ_values, 10):.2f} → {np.percentile(λ_star, 90)/np.percentile(λ_star, 10):.2f}") - h0i_alloc = np.array([[0.0]]) -pre = allocation_from_weights(paths, econ, Ub_list, μ_values, γ_1, Λ, h0i_alloc) -post = allocation_from_weights(paths, econ, Ub_list, λ_star, γ_1, Λ, h0i_alloc) +pre = allocation_from_weights(paths, econ, + Ub_list, μ_values, γ_1, Λ, h0i_alloc) +post = allocation_from_weights(paths, econ, + Ub_list, λ_star, γ_1, Λ, h0i_alloc) c_pre = pre["c"][:, t0:] y_pre = pre["y_net"][:, t0:] From 6f9b1d95702547a2a92e96a93f2b58aad77cf2d9 Mon Sep 17 00:00:00 2001 From: thomassargent30 Date: Fri, 26 Dec 2025 17:45:13 +0800 Subject: [PATCH 10/17] Tom's edits of Humphrey's excellent Gorman lecture, Dec 26 --- lectures/gorman_heterogeneous_households.md | 197 +++++++++++++------- 1 file changed, 125 insertions(+), 72 deletions(-) diff --git a/lectures/gorman_heterogeneous_households.md b/lectures/gorman_heterogeneous_households.md index 66e0cbc9..9d29ac69 100644 --- a/lectures/gorman_heterogeneous_households.md +++ b/lectures/gorman_heterogeneous_households.md @@ -23,13 +23,44 @@ kernelspec: ```{index} single: python ``` -# Gorman Heterogeneous Households and Limited Markets +# Gorman Aggregation -This lecture implements the Gorman heterogeneous-household economy in Chapter 12 of {cite:t}`HansenSargent2013` using the `quantecon.DLE` class. +{cite:t}`gorman1953community` described a class of preferences with the useful property that there exists a 'representative household' +in the sense that competitive equilibrium allocations can be computed by following recursive procedure: + +* take the heterogenous preferences of a diverse collection of households and from them synthesize the preferences of a single hypothetical 'representative household' +* collect the endowments of all households and give them to the representative household +* construct a competitive equilibrium allocation and price system for the representative agent economy +* at the competitive equilibrium price system, compute the wealth -- i.e., the present value -- of each household's initial endowment +* find a consumption plan for each household that maximizes its utility functional subject to the wealth that you computed in the previous step + + +This procedure allows us to compute the competitive equilibrium price system for our heterogeneous household economy **prior** to computing the +competitive equilibrium allocation. + +That substantially simplifies calculating a competitive equilibrium. + +```{note} +In general, computing a competitive equilibrium requires solving for the price system and the allocation simultaneously, not recursively. +``` + + + + + +This lecture uses the `quantecon.DLE` class to study economies that satisfy necessary conditions for Gorman +aggregation of preferences. + +Chapter 12 of {cite:t}`HansenSargent2013` described how to adapt the preference specifications of {cite:t}`gorman1953community` +to the linear-quadratic class of environments assumed in their book. + +The first step in implementing the above recursive algorithm will be to form a representative agent economy and then apply our DLE tools to compute its competitive equilibrium. + +Thus, this lecture builds on tools and Python code described in {doc}`hs_recursive_models`, {doc}`growth_in_dles`, and {doc}`irfs_in_hall_model` + + -It complements {doc}`hs_recursive_models`, {doc}`growth_in_dles`, and {doc}`irfs_in_hall_model` by focusing on how to recover household allocations and portfolios from an aggregate DLE solution. -The headline result is that a complete-markets allocation can be implemented with a mutual fund and a one-period bond when Gorman aggregation holds. In addition to what's in Anaconda, this lecture uses the `quantecon` library @@ -51,47 +82,55 @@ import matplotlib.pyplot as plt ## Overview -Gorman aggregation lets us solve heterogeneous-household economies in two steps: solve a representative-agent linear-quadratic planning problem for aggregates, then recover household allocations via a "sharing rule" with household-specific deviation terms. +When conditions for Gorman aggregation of preferences are satisfied, we can compute a competitive equilibrium of heterogeneous-household economy in two steps: solve a representative-agent linear-quadratic planning problem for aggregates, then recover household allocations via a sharing-formula that makes each household's consumption a household-specific constant share of aggregate consumption. + +* a household's share parameter will depend on the implicit Pareto weight implied by the initial distribution of endowment. -The key is that Gorman conditions ensure all consumers have parallel Engel curves, so aggregate allocations and prices can be determined independently of distribution. +The key is that the Gorman-aggregation conditions ensure all households have parallel Engel curves. -This eliminates the standard problem where the utility possibility frontier shifts with endowment changes, making it impossible to rank allocations without specifying distributional weights. +That feature is what lets us determine the aggregate allocations and price sytem before we compute the distribution of the aggregate allocation among individual households. + +This eliminates the usual feature that the utility possibility frontier shifts with endowment changes, making it impossible to rank allocations without specifying distributional weights. ```{note} Computing a competitive equilibrium typically entails solving a fixed point problem. {cite:t}`negishi1960welfare` described a social welfare function that is maximized, subject to resource and technological constraints, by a competitive equilibrium allocation. -For Negishi, that social welfare function is a "linear combination of the individual utility functions of consumers, with the weights in the combination in inverse proportion to the marginal utilities of income." +For Negishi, that social welfare function is a "linear combination of the individual utility functions of households, with the weights in the combination in inverse proportion to the marginal utilities of income." Because Negishi's weights depend on the allocation through the marginal utilities of income, computing a competitive equilibrium via constrained maximization of a Negishi-style welfare function requires finding a fixed point in the weights. When they apply, the beauty of Gorman's aggregation conditions is that time series aggregates and market prices can be computed *without* resorting to Negishi's fixed point approach. ``` -With the help of this powerful result, we proceed in three steps in this lecture: +With the help of this powerful result, we proceed in two steps: 1. Solve the planner's problem and compute selection matrices that map the aggregate state into allocations and prices. 2. Compute household-specific policies and the Gorman sharing rule. -3. Implement the same Arrow-Debreu allocation using only a mutual fund (aggregate stock) and a one-period bond. -We then simulate examples with two and many households. -Before looking into those details, we first introduce Gorman aggregation in a static economy. +For the special Section 12.6 {cite:t}`HansenSargent2013` case in which preference shocks are inactive, we can also + +3. Implement the Arrow-Debreu allocation using only a mutual fund (aggregate stock) and a one-period bond. + +We shall provide examples of these steps in economies with two and many households. + +Before studying these things in the context of the DLE class, we first introduce Gorman aggregation in a static economy. ### Gorman aggregation in a static economy -To see where the sharing rule comes from, start with a static economy with $n$ goods, price vector $p$, and consumers $j = 1, \ldots, J$. +To see where the sharing rule comes from, start with a static economy with $n$ goods, price vector $p$, and households $j = 1, \ldots, J$. -Let $c^a$ denote the aggregate amount of consumption to be allocated among consumers. +Let $c^a$ denote the aggregate amount of consumption to be allocated among households. Associated with $c^a$ is an Edgeworth box and a set of Pareto optimal allocations. -From the Pareto optimal allocations, one can construct utility allocation surfaces that describe the frontier of alternative feasible utility assignments to individual consumers. +From the Pareto optimal allocations, one can construct utility allocation surfaces that describe the frontier of alternative feasible utility assignments to individual households. Imagine moving from the aggregate vector $c^a$ to some other vector $\tilde{c}^a$ and hence to a new Edgeworth box. -If neither the original box nor the new box contains the other, then it is possible that the utility allocation surfaces for the two boxes may *cross*, in which case there exists no ordering of aggregate consumption that is independent of the utility weights assigned to individual consumers. +If neither the original box nor the new box contains the other, then it is possible that the utility allocation surfaces for the two boxes may *cross*, in which case there exists no ordering of aggregate consumption that is independent of the utility weights assigned to individual households. Here is an example showing when aggregation fails. @@ -254,10 +293,12 @@ In the dynamic setting, we set the utility index $u^j$ equal to household $j$'s The ratio $\mu_{0j}^w / \mu_{0a}^w$ (where $\mu_{0a}^w = \sum_j \mu_{0j}^w$) then serves as the time-invariant Gorman weight that determines household $j$'s share of aggregate consumption in excess of baseline. -Without spoiling further, we now introduce the basic setup for the dynamic heterogeneous-household economy. -## Set up + +## Dynamic, Stochastic Economy + +We now introduce our basic setup for our dynamic heterogeneous-household economy. Time is discrete, $t = 0,1,2,\dots$. @@ -329,11 +370,11 @@ Shadow-price mappings $M_c, M_k, \ldots$ are used to value streams and recover e ### The individual household problem -Recall that we operate in an economy with $J$ consumers indexed by $j = 1, 2, \ldots, J$. +Recall that we operate in an economy with $J$ households indexed by $j = 1, 2, \ldots, J$. -Consumers differ in preferences and endowments but share a common information set. +Households differ in preferences and endowments but share a common information set. -Consumer $j$ maximizes +Household $j$ maximizes $$ -\frac{1}{2} \mathbb{E}_0 \sum_{t=0}^\infty \beta^t \left[(s_{jt} - b_{jt})^\top (s_{jt} - b_{jt}) + \ell_{jt}^\top \ell_{jt}\right] @@ -369,7 +410,7 @@ These prices are determined in equilibrium. In the DLE implementation below, these time-0 prices are recovered from the aggregate solution and encoded in the pricing objects `M_c`, `M_d`, `M_g`, and `M_k`. -This specification confines heterogeneity among consumers to four sources: +This specification confines heterogeneity among households to four sources: 1. Differences in the preference processes $\{b_{jt}\}$, represented by different selections of $U_b^j$. @@ -402,7 +443,7 @@ $$ (eq:agg_preference_aggregates) Aggregates are economy-wide totals: $c_t := \sum_j c_{jt}$, $b_t := \sum_j b_{jt}$, $d_t := \sum_j d_{jt}$, and similarly for $(i_t, k_t, h_t, s_t, g_t)$. -The planner maximizes +The planner maximizes the utility functional of the representative household $$ -\frac{1}{2} \mathbb{E}_0 \sum_{t=0}^\infty \beta^t @@ -451,7 +492,7 @@ These shadow prices correspond to competitive equilibrium prices. (sharing_rules)= ### Consumption sharing rules -This section presents the Gorman consumption sharing rules that's in the headline of this lecture. +This section presents Gorman consumption sharing rules. Our preference specification is an infinite-dimensional generalization of the static Gorman setup described above, where goods are indexed by both dates and states of the world. @@ -465,7 +506,7 @@ $$ The ratio $\mu_{0j}^w / \mu_{0a}^w$ is time-invariant and depends only on information available at time zero. -For notational convenience, we write +For convenience, we write $$ \mu_j := \frac{\mu_{0j}^w}{\mu_{0a}^w}, @@ -500,9 +541,9 @@ The beauty of this representation is that the weight $\mu_j$ is time-invariant. Nonseparabilities in preferences (over time or across goods) affect only the construction of the baseline process $\{\chi_{jt}\}$ and the calculation of the weight $\mu_j$ implied by the distribution of wealth. -#### Service allocation rule +#### Household services allocation rule -The first-order conditions for consumption services give +First-order necessary conditions for consumption services imply $$ s_{jt} - b_{jt} = \mu_{0j}^w \rho_{0t}, @@ -522,7 +563,7 @@ This representation does not involve prices directly. #### Inverse canonical representation -Given $\rho_{0t}$ from the aggregate solution, we can solve {eq}`eq:foc_services` for $s_{jt}$, then use the inverse canonical representation to compute $c_{jt}$ and $h_{jt}$: +Given $\rho_{0t}$ from the aggregate solution, we can solve {eq}`eq:foc_services` for $s_{jt}$, then use the {cite:t}`HansenSargent2013` chapter 9 inverse canonical representation to compute $c_{jt}$ and $h_{jt}$: $$ \begin{aligned} @@ -535,7 +576,7 @@ with $h_{j,-1}$ given. This recursion requires a canonical household technology with square, invertible $\Pi_h$. -#### Deviation form +#### Computational details Define deviation processes $\tilde{y}_{jt} := y_{jt} - \mu_j y_t$ for any variable $y$. @@ -552,7 +593,7 @@ $$ (eq:inverse_canonical_dev) with $\tilde{h}_{j,-1} = h_{j,-1} - \mu_j h_{-1}$ given. -Associated with $\tilde{s}_{jt}$ is a synthetic consumption process $\tilde{\chi}_{jt}$ such that $\tilde{c}_{jt} = \tilde{\chi}_{jt}$ is the optimal sharing rule. +Associated with $\tilde{s}_{jt}$ is a synthetic consumption process $\tilde{\chi}_{jt}$ that makes $\tilde{c}_{jt} = \tilde{\chi}_{jt}$ be the optimal sharing rule. To construct $\tilde{\chi}_{jt}$, we substitute $\tilde{s}_{jt} = \tilde{b}_{jt}$ into the inverse canonical representation: @@ -577,13 +618,13 @@ we can append the recursion for $c_t$ from the aggregate problem to obtain a rec #### Labor allocation rule -The allocation rule for household $j$'s labor (the "intermediate good" $g_{jt} = \ell_{jt}$) is +The allocation rule for household $j$'s labor (i.e., the "intermediate good" $g_{jt} = \ell_{jt}$) is $$ \ell_{jt} = \mu_j \, g_t, $$ (eq:labor_allocation) -where $g_t = \sum_j \ell_{jt}$ is aggregate labor. +where $g_t = \sum_j \ell_{jt}$ is aggregate the aggregate intermediate good. #### Risk sharing @@ -599,13 +640,13 @@ For a fixed Lagrange multiplier $\mu_{0j}^w$ on the household's budget constrain One could compute individual allocations by iterating to find the multiplier $\mu_{0j}^w$ that satisfies the budget constraint. -But there is a more elegant approach using Gorman's allocation rules. +But there is a more elegant approach that uses our Gorman allocation rules. ### Computing household allocations -We apply the consumption sharing rules {eq}`eq:sharing_rule` and {eq}`eq:sharing_rule_baseline` developed above in two steps. +We apply our consumption sharing rules {eq}`eq:sharing_rule` and {eq}`eq:sharing_rule_baseline` in two steps. -#### Compute deviation consumption +#### Step 1: compute deviation consumption Using the inverse canonical representation {eq}`eq:inverse_canonical`, we compute the deviation consumption process $\{\tilde{\chi}_{jt}\}$ from household $j$'s deviation bliss points $\{\tilde{b}_{jt}\}$ and initial deviation state $\tilde{\eta}_{j,-1} = h_{j,-1} - \mu_j h_{-1}$. @@ -613,7 +654,7 @@ When preferences include durables or habits ($\Lambda \neq 0$), the deviation co The code solves this as a linear-quadratic control problem using a scaling trick: multiplying the transition matrices by $\sqrt{\beta}$ converts the discounted problem into an undiscounted one that can be solved with a standard discrete algebraic Riccati equation. -#### Compute the Gorman weight +#### Step 2: compute each Gorman weight We compute the Gorman weight $\mu_j$ using household $j$'s budget constraint. @@ -625,7 +666,7 @@ We compute five present values: - $W_{c2}$: present value of the deviation consumption stream $\{\tilde{\chi}_{jt}\}$ - $W_g$: present value of the intermediate good stream $\{g_t\}$ -Formally (suppressing state indices inside the dot products), +Formally, (suppressing state indices inside the dot products), $$ \begin{aligned} @@ -637,7 +678,7 @@ W_g &= \mathbb{E}_0 \sum_{t=0}^\infty \beta^t \, w_{0t} \, g_t. \end{aligned} $$ -Each present value is computed using `doublej2` below to sum infinite series. +We compute each present value by using `doublej2` to sum infinite series. The budget constraint {eq}`eq:hh_budget` requires that the present value of consumption equals wealth: @@ -663,9 +704,9 @@ $$ \mu_j = \frac{W_k + W_d - W_{c2}}{W_{c1} - W_g}. $$ -The numerator is household $j$'s wealth (initial capital plus present value of endowments minus the cost of the deviation consumption stream). +The numerator is household $j$'s wealth -- i.e., initial capital plus present value of endowments minus the cost of the deviation consumption stream. -The denominator is the net cost of consuming one unit of aggregate consumption (consumption value minus intermediate good value). +The denominator is the net cost of consuming one unit of aggregate consumption, i.e., the value of consumption value minus the value of the intermediate good supplied. Finally, the code constructs selection matrices $S_{ci}, S_{hi}, S_{si}$ that map the augmented state $X_t = [h_{j,t-1}^\top, x_t^\top]^\top$ into household $j$'s allocations: @@ -673,7 +714,7 @@ $$ c_{jt} = S_{ci} X_t, \quad h_{jt} = S_{hi} X_t, \quad s_{jt} = S_{si} X_t. $$ -The augmented state includes household $j$'s own lagged durable stock $h_{j,t-1}$ because the deviation term $\tilde{\chi}_{jt}$ depends on it through the inverse canonical representation. + Because the deviation term $\tilde{\chi}_{jt}$ depends on it through the inverse canonical representation, the augmented state includes household $j$'s own lagged durable stock $h_{j,t-1}$. ```{code-cell} ipython3 def doublej2(A1, B1, A2, B2, tol=1e-15, max_iter=10_000): @@ -870,7 +911,11 @@ def heter( ## Limited markets -This section implements a key result: the Arrow-Debreu allocation can be replicated using only a mutual fund and a one-period bond, rather than a complete set of contingent claims. + + +This section studies a special Section 12.6 {cite:t}`HansenSargent2013` case in which the Arrow-Debreu allocation can be implemented by opening competitive markets only in a mutual fund and a one-period bond. + + * so in our setting, we don't literally require that markets in a complete set of contingent claims be present. To match the implementation result in Chapter 12.6 of {cite:t}`HansenSargent2013`, we specialize to the one-good, constant-return case @@ -880,11 +925,11 @@ k_t = \delta_k k_{t-1} + i_t, \qquad \beta = \frac{1}{\gamma_1 + \delta_k}. $$ -In this case the one-period riskless gross return is constant, $R = \gamma_1 + \delta_k = 1/\beta$. +In this case, the one-period riskless gross return is constant, $R = \gamma_1 + \delta_k = 1/\beta$. -We also impose the Chapter 12.6 measurability restriction that the deviation baseline $\{\tilde{\chi}_{jt}\}$ is $\mathcal{J}_0$-measurable (for example, the preference-shock processes $\{b_{jt}\}$ are deterministic). +We also impose the Chapter 12.6 {cite:t}`HansenSargent2013` measurability restriction that the deviation baseline $\{\tilde{\chi}_{jt}\}$ is $\mathcal{J}_0$-measurable (for example, the preference-shock processes $\{b_{jt}\}$ are deterministic). -### The trading mechanism +### The trading arrangement Consider again an economy where each household $j$ initially owns claims to its own endowment stream $\{d_{jt}\}$. @@ -906,7 +951,7 @@ The bond position $\hat{k}_{jt}$ evolves each period according to the recursion The portfolio weight $\mu_j$ is not arbitrary: it is the unique weight that allows the limited-markets portfolio to replicate the Arrow-Debreu consumption allocation. -Under Gorman aggregation, household $j$'s equilibrium consumption satisfies +With Gorman aggregation, household $j$'s equilibrium consumption satisfies $$ c_{jt} = \mu_j c_t + \tilde{\chi}_{jt}. @@ -978,9 +1023,9 @@ $$ \hat{k}_{jt} = R \hat{k}_{j,t-1} - \tilde{\chi}_{jt}. $$ (eq:bond-recursion) -This says that the bond position grows at rate $R$ but is drawn down by the deviation consumption $\tilde{\chi}_{jt}$. +Equation {eq}`eq:bond-recursion` says that the bond position grows at rate $R$ but is drawn down by the deviation consumption $\tilde{\chi}_{jt}$. -When $\tilde{\chi}_{jt} > 0$ (household $j$ consumes more than its share), it finances this by running down its bond holdings. +When $\tilde{\chi}_{jt} > 0$ (i.e., household $j$ consumes more than its share), it finances this by running down its bond holdings. The household's total asset position is hence $a_{jt} = \mu_j k_t + \hat{k}_{jt}$. @@ -1000,7 +1045,9 @@ $$ \end{aligned} $$ -For the budget constraint to hold with equality, we apply transversality and require $\lim_{T \to \infty} R^{-T} \hat{k}_{jT} = 0$. Dividing by $R^T$: +For the budget constraint to hold with equality, we apply transversality and require $\lim_{T \to \infty} R^{-T} \hat{k}_{jT} = 0$. + +Dividing by $R^T$: $$ R^{-T} \hat{k}_{jT} = \hat{k}_{j0} - \sum_{t=1}^T R^{-t} \tilde{\chi}_{jt} @@ -1024,18 +1071,18 @@ Under the measurability restriction ($\tilde{\chi}_{jt} \in \mathcal{J}_0$), thi ```{note} -This construction displays a multiperiod counterpart to an aggregation result for security markets derived by {cite:t}`rubinstein1974aggregation`. +This construction embodies a multiperiod counterpart to an aggregation result for security markets derived by {cite:t}`rubinstein1974aggregation`. -In a two-period model, Rubinstein provided sufficient conditions on preferences of consumers and asset market payoffs for the implementation of an Arrow-Debreu contingent claims allocation with incomplete security markets. +In a two-period model, Rubinstein provided sufficient conditions on preferences of households and asset market payoffs that implement a complete-markets Arrow-Debreu contingent claims allocation with incomplete security markets. -In Rubinstein's implementation, all consumers hold the same portfolio of risky assets. +In Rubinstein's implementation, all households hold the same portfolio of risky assets. -In our construction, consumers also hold the same portfolio of risky assets, and portfolio weights do not vary over time. +In our construction, households also hold the same portfolio of risky assets, and portfolio weights do not vary over time. All changes over time in portfolio composition take place through transactions in the bond market. ``` -Below is the code that computes household allocations and limited-markets portfolios along a fixed aggregate path according to the mechanism described above +The code below computes household allocations and limited-markets portfolios according to the market structure we have just described. ```{code-cell} ipython3 def compute_household_paths( @@ -1189,10 +1236,10 @@ def compute_household_paths( } ``` -The next function collects everything to solve the planner's problem and compute household paths +The next function collects objects required to solve the planner's problem and compute household paths into one function. -We first use the `DLE` class to set up the representative-agent DLE problem. +First we use the `DLE` class to set up the representative-agent DLE problem. Then we build the full initial state by stacking zeros for lagged durables and capital with the initial exogenous state. @@ -1233,7 +1280,7 @@ def solve_model(info, tech, pref, U_b_list, U_d_list, γ_1, Λ, z0, ts_length=20 (gorman_twohh)= ## Example: two-household economy -We reproduce the two-household example from Chapter 12.6 of {cite:t}`HansenSargent2013`. +We now reproduce a two-household example from Chapter 12.6 of {cite:t}`HansenSargent2013`. There are two households, each with preferences @@ -1372,7 +1419,7 @@ ax.legend() plt.show() ``` -The final check is to verify that the bond positions sum to zero at all times, confirming that the limited-markets implementation is self-financing +Our final check is to verify that the bond positions sum to zero at all times, confirming that the limited-markets implementation is self-financing ```{code-cell} ipython3 np.max(np.abs(paths["k_hat"].sum(axis=0))) @@ -1440,13 +1487,13 @@ $$ \sum_{j=1}^J d_{j,t} = \sum_{j=1}^J \alpha_j + d_{a,t}. $$ -Preference shocks are muted to simplify experiments: +To activate the assumptions behind our limited-markets arrangement for implementing an Arrow-Debreu allocation, we silence preference shocks by setting: $$ b_{jt} = \bar{b} + \xi_{j,t}, \quad j = 1, \ldots, J, $$ -where the $\xi_{j,t}$ processes are taken to be small (and can be shut off entirely by setting their innovation loadings to zero). +where the $\xi_{j,t}$ processes can be shut silenced by setting their innovation loadings to zero. To complete the specification, we let the idiosyncratic states follow AR(1) laws of motion (all innovations are components of the i.i.d. vector $w_{t+1}$ in $z_{t+1} = A_{22} z_t + C_2 w_{t+1}$): @@ -1593,11 +1640,11 @@ def build_reverse_engineered_gorman_extended( return A22, C2, Ub, Ud, Ub_per_house, Ud_per_house, x0 ``` -### 100-household reverse engineered economy +### 100-household economy -We now instantiate a 100-household economy using the setup above. +We now instantiate a 100-household version of the preceding economy. -We use the same technology and preference parameters as the two-household example. +We use the same technology and preference parameters. ```{code-cell} ipython3 # Technology and preference parameters @@ -1839,7 +1886,7 @@ plt.show() The aggregate endowment shock $d_{a,t}$ follows an AR(1) process with $\rho_1 = 0.95$, so after the initial impact it decays monotonically toward zero as $0.95^t$. -Capital, by contrast, is an endogenous stock that accumulates when the planner smooths consumption. +By contrast, capital is an endogenous stock that accumulates when the planner smooths consumption. The positive endowment shock increases resources temporarily, but Hall-style preferences imply the planner saves part of the windfall rather than consuming it immediately. @@ -1890,9 +1937,15 @@ plt.show() Indeed, the average of individual household endowments tracks the aggregate endowment process, confirming that the construction of the idiosyncratic shocks and absorbing households works as intended. -## Redistribution via Pareto weight reallocation +## Redistributing by adjusting Pareto weights -This section analyzes tax-and-transfer schemes by reinterpreting competitive equilibrium allocations in terms of Pareto weights, then considering alternative weight distributions that redistribute consumption while preserving aggregate dynamics. +This section analyzes Pareto-efficient tax-and-transfer schemes by simply taking competitive equilibrium allocations and then use +a set of nonnegative Pareto weights that sum to one. + +```{note} +There are various schemes that would deliver such efficient efficient redistributions, but in terms of what interests us in this example, +they are all equivalent. +``` ### Redistribution via Pareto weights @@ -1913,7 +1966,7 @@ $$ y_{j,t}(\omega) = \omega_j d_t + (R-1) a_{j,t-1}. $$ -In this section, we compare pre- and post-redistribution consumption and income. +We want to compare pre- and post-redistribution consumption and income. Let $\mu := \{\mu_j\}_{j=1}^J$ denote the original competitive equilibrium Pareto weights, and let $\lambda^* := \{\lambda_j^*\}_{j=1}^J$ denote the redistributed weights. @@ -1926,7 +1979,7 @@ To implement a redistribution from $\mu$ to $\lambda^*$, we construct new Pareto 3. Leave middle-$j$ types relatively unaffected 4. Preserve the constraint $\sum_{j=1}^J \lambda_j^* = 1$ -We implement this using a smooth transformation. +We choose to implement these steps by using a smooth transformation. Let $\{\lambda_j\}_{j=1}^J$ denote the original competitive equilibrium Pareto weights (sorted in descending order). Define the redistribution function: @@ -1941,7 +1994,7 @@ where: - $\alpha > 0$ controls the overall magnitude of redistribution - $\beta$ controls the progressivity (higher $\beta$ concentrates redistribution more strongly in the tails) -The redistributed Pareto weights are: +Post-redistribution Pareto weights are: $$ \tilde{\lambda}_j = \lambda_j + \tau(j; J, \alpha, \beta) \cdot (\bar{\lambda} - \lambda_j), @@ -1949,7 +2002,7 @@ $$ where $\bar{\lambda} = J^{-1}$ is the equal-weight benchmark. -To ensure the weights sum to unity, we normalize: +To ensure that our weights sum to unity, we normalize: $$ \lambda_j^* = \frac{\tilde{\lambda}_j}{\sum_{k=1}^J \tilde{\lambda}_k}. @@ -2161,8 +2214,8 @@ plt.tight_layout() plt.show() ``` -The figures shows a clear reduction in income and consumption inequality after redistribution via Pareto weights. +The figures reveal a striking reduction in income and consumption inequality after redistribution. -We also see how insurance smooths consumption relative to income, especially for lower-percentile households. +Also, notice how insurance smooths consumption relative to income, especially for lower-percentile households. The post-tax-and-transfer income path exhibits less volatility for the lower percentiles compared to the pre-tax-and-transfer income. From 82de8c94e5fb3bbaaca03d4b618e90507e004684 Mon Sep 17 00:00:00 2001 From: Humphrey Yang Date: Sat, 27 Dec 2025 11:29:04 +1100 Subject: [PATCH 11/17] updates --- lectures/gorman_heterogeneous_households.md | 30 ++++++++++----------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/lectures/gorman_heterogeneous_households.md b/lectures/gorman_heterogeneous_households.md index 9d29ac69..a40a037a 100644 --- a/lectures/gorman_heterogeneous_households.md +++ b/lectures/gorman_heterogeneous_households.md @@ -26,9 +26,9 @@ kernelspec: # Gorman Aggregation {cite:t}`gorman1953community` described a class of preferences with the useful property that there exists a 'representative household' -in the sense that competitive equilibrium allocations can be computed by following recursive procedure: +in the sense that competitive equilibrium allocations can be computed by following a recursive procedure: -* take the heterogenous preferences of a diverse collection of households and from them synthesize the preferences of a single hypothetical 'representative household' +* take the heterogeneous preferences of a diverse collection of households and from them synthesize the preferences of a single hypothetical 'representative household' * collect the endowments of all households and give them to the representative household * construct a competitive equilibrium allocation and price system for the representative agent economy * at the competitive equilibrium price system, compute the wealth -- i.e., the present value -- of each household's initial endowment @@ -56,7 +56,7 @@ to the linear-quadratic class of environments assumed in their book. The first step in implementing the above recursive algorithm will be to form a representative agent economy and then apply our DLE tools to compute its competitive equilibrium. -Thus, this lecture builds on tools and Python code described in {doc}`hs_recursive_models`, {doc}`growth_in_dles`, and {doc}`irfs_in_hall_model` +Thus, this lecture builds on tools and Python code described in {doc}`hs_recursive_models`, {doc}`growth_in_dles`, and {doc}`irfs_in_hall_model`. @@ -88,7 +88,7 @@ When conditions for Gorman aggregation of preferences are satisfied, we can comp The key is that the Gorman-aggregation conditions ensure all households have parallel Engel curves. -That feature is what lets us determine the aggregate allocations and price sytem before we compute the distribution of the aggregate allocation among individual households. +That feature is what lets us determine the aggregate allocations and price system before we compute the distribution of the aggregate allocation among individual households. This eliminates the usual feature that the utility possibility frontier shifts with endowment changes, making it impossible to rank allocations without specifying distributional weights. @@ -110,7 +110,7 @@ With the help of this powerful result, we proceed in two steps: 2. Compute household-specific policies and the Gorman sharing rule. -For the special Section 12.6 {cite:t}`HansenSargent2013` case in which preference shocks are inactive, we can also +For the special case in Section 12.6 of {cite:t}`HansenSargent2013`, where preference shocks are inactive, we can also 3. Implement the Arrow-Debreu allocation using only a mutual fund (aggregate stock) and a one-period bond. @@ -563,7 +563,7 @@ This representation does not involve prices directly. #### Inverse canonical representation -Given $\rho_{0t}$ from the aggregate solution, we can solve {eq}`eq:foc_services` for $s_{jt}$, then use the {cite:t}`HansenSargent2013` chapter 9 inverse canonical representation to compute $c_{jt}$ and $h_{jt}$: +Given $\rho_{0t}$ from the aggregate solution, we can solve {eq}`eq:foc_services` for $s_{jt}$, then use the inverse canonical representation from Chapter 9 of {cite:t}`HansenSargent2013` to compute $c_{jt}$ and $h_{jt}$: $$ \begin{aligned} @@ -624,7 +624,7 @@ $$ \ell_{jt} = \mu_j \, g_t, $$ (eq:labor_allocation) -where $g_t = \sum_j \ell_{jt}$ is aggregate the aggregate intermediate good. +where $g_t = \sum_j \ell_{jt}$ is the aggregate intermediate good. #### Risk sharing @@ -706,7 +706,7 @@ $$ The numerator is household $j$'s wealth -- i.e., initial capital plus present value of endowments minus the cost of the deviation consumption stream. -The denominator is the net cost of consuming one unit of aggregate consumption, i.e., the value of consumption value minus the value of the intermediate good supplied. +The denominator is the net cost of consuming one unit of aggregate consumption, i.e., the value of consumption minus the value of the intermediate good supplied. Finally, the code constructs selection matrices $S_{ci}, S_{hi}, S_{si}$ that map the augmented state $X_t = [h_{j,t-1}^\top, x_t^\top]^\top$ into household $j$'s allocations: @@ -913,9 +913,9 @@ def heter( -This section studies a special Section 12.6 {cite:t}`HansenSargent2013` case in which the Arrow-Debreu allocation can be implemented by opening competitive markets only in a mutual fund and a one-period bond. +This section studies the special Section 12.6 {cite:t}`HansenSargent2013` case in which the Arrow-Debreu allocation can be implemented by opening competitive markets only in a mutual fund and a one-period bond. - * so in our setting, we don't literally require that markets in a complete set of contingent claims be present. + * So in our setting, we don't literally require that markets in a complete set of contingent claims be present. To match the implementation result in Chapter 12.6 of {cite:t}`HansenSargent2013`, we specialize to the one-good, constant-return case @@ -1493,7 +1493,7 @@ $$ b_{jt} = \bar{b} + \xi_{j,t}, \quad j = 1, \ldots, J, $$ -where the $\xi_{j,t}$ processes can be shut silenced by setting their innovation loadings to zero. +where the $\xi_{j,t}$ processes can be silenced by setting their innovation loadings to zero. To complete the specification, we let the idiosyncratic states follow AR(1) laws of motion (all innovations are components of the i.i.d. vector $w_{t+1}$ in $z_{t+1} = A_{22} z_t + C_2 w_{t+1}$): @@ -1828,7 +1828,7 @@ n_k = np.atleast_2d(econ.thetak).shape[0] n_endo = n_h + n_k # endogenous state dimension ``` -With the state space representation, we can compute impulse responses to show how shocks propagate through the economy. +With the state-space representation, we can compute impulse responses to show how shocks propagate through the economy. To trace the impulse response to shock $j$, we set `shock_idx=j` which selects column $j$ of the loading matrix $C$. @@ -1892,7 +1892,7 @@ The positive endowment shock increases resources temporarily, but Hall-style pre This causes capital to rise as $d_{a,t}$ falls — this is permanent income logic at work. -Next, we examine if the household consumption and endowment paths generated by the simulation obey the Gorman sharing rule +Next, we examine whether the household consumption and endowment paths generated by the simulation obey the Gorman sharing rule ```{code-cell} ipython3 c_j_t0 = paths["c_j"][..., t0:] @@ -1939,11 +1939,11 @@ Indeed, the average of individual household endowments tracks the aggregate endo ## Redistributing by adjusting Pareto weights -This section analyzes Pareto-efficient tax-and-transfer schemes by simply taking competitive equilibrium allocations and then use +This section analyzes Pareto-efficient tax-and-transfer schemes by simply taking competitive equilibrium allocations and then using a set of nonnegative Pareto weights that sum to one. ```{note} -There are various schemes that would deliver such efficient efficient redistributions, but in terms of what interests us in this example, +There are various schemes that would deliver such efficient redistributions, but in terms of what interests us in this example, they are all equivalent. ``` From 81fbfae923fe9c1ec40bcb4c16c264b4686a2f34 Mon Sep 17 00:00:00 2001 From: Humphrey Yang Date: Sat, 27 Dec 2025 15:50:21 +1100 Subject: [PATCH 12/17] minor updates --- lectures/gorman_heterogeneous_households.md | 89 +++++++++------------ 1 file changed, 38 insertions(+), 51 deletions(-) diff --git a/lectures/gorman_heterogeneous_households.md b/lectures/gorman_heterogeneous_households.md index a40a037a..e666aecd 100644 --- a/lectures/gorman_heterogeneous_households.md +++ b/lectures/gorman_heterogeneous_households.md @@ -4,7 +4,7 @@ jupytext: extension: .md format_name: myst format_version: 0.13 - jupytext_version: 1.17.1 + jupytext_version: 1.17.3 kernelspec: display_name: Python 3 (ipykernel) language: python @@ -25,17 +25,17 @@ kernelspec: # Gorman Aggregation -{cite:t}`gorman1953community` described a class of preferences with the useful property that there exists a 'representative household' +{cite:t}`gorman1953community` described a class of preferences with the useful property that there exists a "representative household" in the sense that competitive equilibrium allocations can be computed by following a recursive procedure: -* take the heterogeneous preferences of a diverse collection of households and from them synthesize the preferences of a single hypothetical 'representative household' +* take the heterogeneous preferences of a diverse collection of households and from them synthesize the preferences of a single hypothetical "representative household" * collect the endowments of all households and give them to the representative household * construct a competitive equilibrium allocation and price system for the representative agent economy * at the competitive equilibrium price system, compute the wealth -- i.e., the present value -- of each household's initial endowment * find a consumption plan for each household that maximizes its utility functional subject to the wealth that you computed in the previous step -This procedure allows us to compute the competitive equilibrium price system for our heterogeneous household economy **prior** to computing the +This procedure allows us to compute the competitive equilibrium price system for our heterogeneous household economy *prior* to computing the competitive equilibrium allocation. That substantially simplifies calculating a competitive equilibrium. @@ -118,6 +118,7 @@ We shall provide examples of these steps in economies with two and many househo Before studying these things in the context of the DLE class, we first introduce Gorman aggregation in a static economy. +(static_gorman)= ### Gorman aggregation in a static economy To see where the sharing rule comes from, start with a static economy with $n$ goods, price vector $p$, and households $j = 1, \ldots, J$. @@ -210,7 +211,7 @@ It follows that $\psi_j$ and $\psi_c$ are homogeneous of degree zero in prices, The baseline indifference curves are either the highest or lowest indifference curves, corresponding respectively to cases in which the utility indices $u^j$ are restricted to be nonpositive or nonnegative. -As noted by Gorman, when preferences are of this form, there is a well-defined compensated demand function for a fictitious representative consumer obtained by aggregating individual demands: +As noted by Gorman, when preferences are of this form, there is a well-defined compensated demand function for a fictitious representative agent obtained by aggregating individual demands: $$ c^a = \psi_a(p) + u^a \psi_c(p), @@ -259,7 +260,7 @@ $$ Notice that the coefficient on $W^j$ is the same for all $j$ since $\psi_c(p)/(p \cdot \psi_c(p))$ is a function only of the price vector $p$. -The individual allocations can be determined from the Engel curves by substituting for $p$ the gradient vector obtained from the representative consumer's optimal allocation problem. +The individual allocations can be determined from the Engel curves by substituting for $p$ the gradient vector obtained from the representative agent's optimal allocation problem. In the quadratic specifications used in this lecture (and in {cite:t}`HansenSargent2013`), the baseline components are degenerate in the sense that $\psi_j(p) = \chi^j$ is independent of $p$, where $\chi^j$ is a consumer-specific bliss point represented by a vector with the same dimension as $c^j$. @@ -312,7 +313,7 @@ The exogenous state $z_t$ follows a first-order vector autoregression $$ z_{t+1} = A_{22} z_t + C_2 w_{t+1}, -$$ +$$ (eq:exogenous_var) where $A_{22}$ governs persistence and $C_2$ maps i.i.d. shocks $w_{t+1}$ into the state. @@ -342,7 +343,7 @@ The economy's resource constraint is $$ \Phi_c c_t + \Phi_g g_t + \Phi_i i_t = \Gamma k_{t-1} + d_t, \quad k_t = \Delta_k k_{t-1} + \Theta_k i_t, -$$ +$$ (eq:resource_constraint) and @@ -351,7 +352,7 @@ h_t = \Delta_h h_{t-1} + \Theta_h c_t, \quad s_t = \Lambda h_{t-1} + \Pi_h c_t, \quad b_t = U_b z_t, \quad d_t = U_d z_t. -$$ +$$ (eq:household_service_tech) Here $h_t$ is the aggregate household service, $s_t$ is the aggregate service flow derived from household capital, @@ -363,11 +364,6 @@ In the one-good case used in the limited-markets section, these reduce to scalar We write $\Pi_h$ for the household service-technology matrix $\Pi$ in {cite:t}`HansenSargent2013`. -Selection matrices such as $S_{(q)}$ map the aggregate state $x_t$ into aggregate quantities such as $q_t = S_{(q)} x_t$ -for $q = c, i, k, h, s, g, b, d$. - -Shadow-price mappings $M_c, M_k, \ldots$ are used to value streams and recover equilibrium prices. - ### The individual household problem Recall that we operate in an economy with $J$ households indexed by $j = 1, 2, \ldots, J$. @@ -430,7 +426,7 @@ These restrictions enable Gorman aggregation by ensuring that household demands This will allow us to solve for aggregate allocations and prices without knowing the distribution of wealth across households as we shall see in {ref}`sharing_rules`. -### The aggregate planning problem +### The representative agent problem We construct aggregates by summing across households: @@ -443,33 +439,30 @@ $$ (eq:agg_preference_aggregates) Aggregates are economy-wide totals: $c_t := \sum_j c_{jt}$, $b_t := \sum_j b_{jt}$, $d_t := \sum_j d_{jt}$, and similarly for $(i_t, k_t, h_t, s_t, g_t)$. -The planner maximizes the utility functional of the representative household +Under the Gorman/LQ restrictions, we can compute equilibrium prices and aggregate quantities by synthesizing a fictitious *representative agent* whose first-order conditions reproduce the competitive equilibrium conditions for aggregates. + +This representative problem is an aggregation device: it is chosen to deliver the correct *aggregate* allocation and prices. + +The representative agent maximizes $$ -\frac{1}{2} \mathbb{E}_0 \sum_{t=0}^\infty \beta^t \left[(s_t - b_t)^\top(s_t - b_t) + g_t^\top g_t\right] $$ (eq:planner_objective) -where $g_t = \sum_j \ell_{jt}$ is aggregate labor supply, subject to technology constraints +subject to the technology constraints {eq}`eq:resource_constraint` and {eq}`eq:household_service_tech`, and the exogenous state process {eq}`eq:exogenous_var`. -$$ -\begin{aligned} -\Phi_c c_t + \Phi_g g_t + \Phi_i i_t &= \Gamma k_{t-1} + d_t, \\ -k_t &= \Delta_k k_{t-1} + \Theta_k i_t, \\ -h_t &= \Delta_h h_{t-1} + \Theta_h c_t, \\ -s_t &= \Lambda h_{t-1} + \Pi_h c_t, -\end{aligned} -$$ (eq:planner_constraints) +The variable $g_t$ is the aggregate intermediate good. -and exogenous processes +With quadratic labor costs and linear budget terms, household $j$'s intratemporal first-order condition implies $\ell_{jt} = \mu^w_{0j} w_{0t}$, where $\mu^w_{0j}$ is household $j$'s time-zero marginal utility of wealth. -$$ -z_{t+1} = A_{22} z_t + C_2 w_{t+1}, \quad -b_t = U_b z_t, \quad -d_t = U_d z_t. -$$ (eq:exogenous_process) +Aggregating gives $g_t := \sum_j \ell_{jt} = \mu^w_{0a} w_{0t}$, where $\mu^w_{0a}=\sum_j \mu^w_{0j}$. -Note that the constraints involve lagged stocks $(h_{t-1}, k_{t-1})$ and the current exogenous state $z_t$. These predetermined variables form the planner's state: +Hence, the representative agent is constructed so that its first-order condition delivers the same aggregate relation $g_t=\mu^w_{0a} w_{0t}$. + +Note that constraints above involve lagged stocks $(h_{t-1}, k_{t-1})$ and the current exogenous state $z_t$. + +These predetermined variables form the planner's state: $$ x_t = [h_{t-1}^\top, k_{t-1}^\top, z_t^\top]^\top. @@ -494,7 +487,7 @@ These shadow prices correspond to competitive equilibrium prices. This section presents Gorman consumption sharing rules. -Our preference specification is an infinite-dimensional generalization of the static Gorman setup described above, where goods are indexed by both dates and states of the world. +Our preference specification is an infinite-dimensional generalization of the static Gorman setup described in {ref}`static_gorman`, where goods are indexed by both dates and states of the world. Let $\mu_{0j}^w$ denote household $j$'s *time-zero marginal utility of wealth*, the Lagrange multiplier on its intertemporal budget constraint. @@ -618,13 +611,15 @@ we can append the recursion for $c_t$ from the aggregate problem to obtain a rec #### Labor allocation rule -The allocation rule for household $j$'s labor (i.e., the "intermediate good" $g_{jt} = \ell_{jt}$) is +Once the aggregate allocation and prices are obtained from the representative problem, individual labor allocations follow from the ratio of individual to aggregate marginal utilities of wealth: $$ -\ell_{jt} = \mu_j \, g_t, +\ell_{jt} = \frac{\mu_{0j}^w}{\mu_{0a}^w} \, g_t \equiv \mu_j \, g_t, $$ (eq:labor_allocation) -where $g_t = \sum_j \ell_{jt}$ is the aggregate intermediate good. +where $g_t$ is the aggregate intermediate good determined by the representative agent's first-order condition. + +This step recovers household labor supplies consistent with the competitive equilibrium. #### Risk sharing @@ -1340,7 +1335,7 @@ U_b1 = np.array([[15.0, 0.0, 0.0, 0.0, 0.0]]) U_b2 = np.array([[15.0, 0.0, 0.0, 0.0, 0.0]]) U_b = U_b1 + U_b2 -# HH1: d_1 = 4 + 0.2ε_2 (idiosyncratic); +# HH1: d_1 = 4 + 0.2ε_1 (idiosyncratic); # HH2: d_2 = 3 + d_tilde2 (aggregate AR(2)) U_d1 = np.array([[4.0, 0.0, 0.0, 0.2, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0]]) U_d2 = np.array([[3.0, 1.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0]]) @@ -1514,7 +1509,7 @@ Here $\varepsilon^{d}_{j,t+1}$ and $\varepsilon^{b}_{j,t+1}$ are mean-zero, unit Setting $\rho^{d}_j = 0$ (or $\rho^{b}_j = 0$) recovers i.i.d. shocks. ```{code-cell} ipython3 -def build_reverse_engineered_gorman_extended( +def build_gorman_extended( n, rho1, rho2, sigma_a, alphas, phis, sigmas, @@ -1696,22 +1691,14 @@ n_absorb = 50 ρ_idio_min, ρ_idio_max = 0.0, 0.98 ρ_idio = ρ_idio_min + (ρ_idio_max - ρ_idio_min) * (poorness[n_absorb:] ** 1.0) -# Preference shocks (disabled by default) +# Preference shocks are muted b_bar = 5.0 -enable_pref_shocks = False -pref_shock_scale = 0.5 -pref_shock_persistence = 0.7 - -if enable_pref_shocks: - γs_pref = pref_shock_scale * np.ones(N) - ρ_pref = pref_shock_persistence -else: - γs_pref = np.zeros(N) - ρ_pref = 0.0 +γs_pref = np.zeros(N) +ρ_pref = 0.0 t0 = 200 -A22, C2, Ub, Ud, Ub_list, Ud_list, x0 = build_reverse_engineered_gorman_extended( +A22, C2, Ub, Ud, Ub_list, Ud_list, x0 = build_gorman_extended( n=N, rho1=ρ1, rho2=ρ2, sigma_a=σ_a, alphas=αs, phis=φs, sigmas=σs, @@ -1949,7 +1936,7 @@ they are all equivalent. ### Redistribution via Pareto weights -The sharing rule {eq}`eq:sharing_rule` can be written as $c_{jt} - \chi_{jt} = \mu_j (c_t - \chi_t)$, where $\mu_j$ is household $j$'s wealth share. +The sharing rule {eq}`eq:sharing_rule` can be written as $c_{jt} - \chi_{jt} = \mu_j (c_t - \chi_t)$, where $\mu_j$ is household $j$'s Gorman weight. Define the Pareto weight $\lambda_j := \mu_j$, with $\sum_{j=1}^J \lambda_j = 1$. From 472b2ac2640fb4a69d3f052393305381eaad80a5 Mon Sep 17 00:00:00 2001 From: Humphrey Yang Date: Sat, 27 Dec 2025 16:02:43 +1100 Subject: [PATCH 13/17] updates --- lectures/gorman_heterogeneous_households.md | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/lectures/gorman_heterogeneous_households.md b/lectures/gorman_heterogeneous_households.md index e666aecd..86c03dcd 100644 --- a/lectures/gorman_heterogeneous_households.md +++ b/lectures/gorman_heterogeneous_households.md @@ -76,7 +76,7 @@ We make the following imports import numpy as np from scipy.linalg import solve_discrete_are from quantecon import DLE -from quantecon._lqcontrol import LQ +from quantecon import LQ import matplotlib.pyplot as plt ``` @@ -400,6 +400,7 @@ Here $\mathbb{E}_0$ is expectation conditional on time-zero information $\mathca The prices are: - $p_{0t}$: the time-0 Arrow-Debreu price vector for date-$t$ consumption goods (across states), so $p_{0t} \cdot c_{jt}$ is the time-0 value of date-$t$ consumption - $w_{0t}$: the time-0 value of date-$t$ labor +- $\alpha_{0t}$: the time-0 price vector for date-$t$ endowments (dividends) - $v_0$: the time-0 value of initial capital These prices are determined in equilibrium. @@ -485,7 +486,7 @@ These shadow prices correspond to competitive equilibrium prices. (sharing_rules)= ### Consumption sharing rules -This section presents Gorman consumption sharing rules. +This section presents the Gorman consumption sharing rules. Our preference specification is an infinite-dimensional generalization of the static Gorman setup described in {ref}`static_gorman`, where goods are indexed by both dates and states of the world. @@ -635,7 +636,7 @@ For a fixed Lagrange multiplier $\mu_{0j}^w$ on the household's budget constrain One could compute individual allocations by iterating to find the multiplier $\mu_{0j}^w$ that satisfies the budget constraint. -But there is a more elegant approach that uses our Gorman allocation rules. +But there is a more elegant approach that uses the Gorman allocation rules. ### Computing household allocations @@ -908,7 +909,7 @@ def heter( -This section studies the special Section 12.6 {cite:t}`HansenSargent2013` case in which the Arrow-Debreu allocation can be implemented by opening competitive markets only in a mutual fund and a one-period bond. +This section studies the special case from Section 12.6 of {cite:t}`HansenSargent2013` in which the Arrow-Debreu allocation can be implemented by opening competitive markets only in a mutual fund and a one-period bond. * So in our setting, we don't literally require that markets in a complete set of contingent claims be present. @@ -1286,19 +1287,19 @@ $$ We set $b^i_t = 15$ for $i = 1, 2$, so the aggregate preference shock is $b_t = \sum_i b^i_t = 30$. The endowment processes are $$ -d^1_t = 4 + 0.2\, w^1_t, \qquad +d^1_t = 4 + 0.2\, \varepsilon^1_t, \qquad d^2_t = 3 + \tilde{d}^2_t, $$ -where $w^1_t$ is Gaussian white noise with variance $(0.2)^2$, and $\tilde{d}^2_t$ follows +where $\varepsilon^1_t$ is standard Gaussian noise (so the idiosyncratic component has standard deviation $0.2$), and $\tilde{d}^2_t$ follows $$ -\tilde{d}^2_t = 1.2\, \tilde{d}^2_{t-1} - 0.22\, \tilde{d}^2_{t-2} + 0.25\, w^2_t, +\tilde{d}^2_t = 1.2\, \tilde{d}^2_{t-1} - 0.22\, \tilde{d}^2_{t-2} + 0.25\, \varepsilon^2_t, $$ -with $w^2_t$ Gaussian white noise with variance $(0.25)^2$. +with $\varepsilon^2_t$ also standard Gaussian noise (so the innovation has standard deviation $0.25$). -Here $w^1_t$ and $w^2_t$ are components of the exogenous innovation vector driving $z_t$ and should not be confused with the wage-price sequence $w_{0t}$ in the household budget constraint. +Here $\varepsilon^1_t$ and $\varepsilon^2_t$ are components of the exogenous innovation vector $w_{t+1}$ driving $z_t$ and should not be confused with the wage-price sequence $w_{0t}$ in the household budget constraint. ```{code-cell} ipython3 # Technology: c + i = γ_1 * k_{t-1} + d, with β = 1/(γ_1 + δ_k) @@ -1850,7 +1851,7 @@ idx_da = n_h + n_k + 1 T_irf = 50 shock_size = 1.0 irf_x, irf_y = compute_irf(A0, C, G, shock_idx=0, T=T_irf, shock_size=shock_size) -idx_k = 4 +idx_k = 4 # index of capital in stacked observable vector G @ x da_irf = irf_x[idx_da, :] From 5d7b20295551185ed8738cc9a665984803d2c1e3 Mon Sep 17 00:00:00 2001 From: thomassargent30 Date: Sat, 27 Dec 2025 16:07:31 +0800 Subject: [PATCH 14/17] Tom's Dec 27 edits of Gorman lecture --- lectures/gorman_heterogeneous_households.md | 101 +++++++++++--------- 1 file changed, 54 insertions(+), 47 deletions(-) diff --git a/lectures/gorman_heterogeneous_households.md b/lectures/gorman_heterogeneous_households.md index 86c03dcd..4a05ee70 100644 --- a/lectures/gorman_heterogeneous_households.md +++ b/lectures/gorman_heterogeneous_households.md @@ -25,8 +25,9 @@ kernelspec: # Gorman Aggregation -{cite:t}`gorman1953community` described a class of preferences with the useful property that there exists a "representative household" -in the sense that competitive equilibrium allocations can be computed by following a recursive procedure: +## Overview + +{cite:t}`gorman1953community` described a class of environments models with preferences having the useful property that there exists a "representative household" in the sense that competitive equilibrium allocations can be computed in the following way: * take the heterogeneous preferences of a diverse collection of households and from them synthesize the preferences of a single hypothetical "representative household" * collect the endowments of all households and give them to the representative household @@ -45,44 +46,30 @@ In general, computing a competitive equilibrium requires solving for the price s ``` - +Chapter 12 of {cite:t}`HansenSargent2013` described how to adapt the preference specifications of {cite:t}`gorman1953community` +to a linear-quadratic class of environments. This lecture uses the `quantecon.DLE` class to study economies that satisfy necessary conditions for Gorman aggregation of preferences. -Chapter 12 of {cite:t}`HansenSargent2013` described how to adapt the preference specifications of {cite:t}`gorman1953community` -to the linear-quadratic class of environments assumed in their book. - -The first step in implementing the above recursive algorithm will be to form a representative agent economy and then apply our DLE tools to compute its competitive equilibrium. - -Thus, this lecture builds on tools and Python code described in {doc}`hs_recursive_models`, {doc}`growth_in_dles`, and {doc}`irfs_in_hall_model`. - +To compute a competitive equilibrium, our first step will be to form a representative agent. -In addition to what's in Anaconda, this lecture uses the `quantecon` library - -```{code-cell} ipython3 -:tags: [hide-output] +After that, we can use some of our DLE tools to compute competitive equilibrium -!pip install --upgrade quantecon -``` - -We make the following imports +* prices without knowing the allocation of consumption to individual households +* households' individual wealth levels +* households' consumption levels +* +Thus, this lecture builds on tools and Python code described in {doc}`hs_recursive_models`, {doc}`growth_in_dles`, and {doc}`irfs_in_hall_model`. -```{code-cell} ipython3 -import numpy as np -from scipy.linalg import solve_discrete_are -from quantecon import DLE -from quantecon import LQ -import matplotlib.pyplot as plt -``` + -## Overview -When conditions for Gorman aggregation of preferences are satisfied, we can compute a competitive equilibrium of heterogeneous-household economy in two steps: solve a representative-agent linear-quadratic planning problem for aggregates, then recover household allocations via a sharing-formula that makes each household's consumption a household-specific constant share of aggregate consumption. +In a little more detail, when conditions for Gorman aggregation of preferences are satisfied, we can compute a competitive equilibrium of heterogeneous-household economy in two steps: solve a representative-agent linear-quadratic planning problem for aggregates, then recover household allocations via a sharing-formula that makes each household's consumption a household-specific constant share of aggregate consumption. * a household's share parameter will depend on the implicit Pareto weight implied by the initial distribution of endowment. @@ -110,18 +97,39 @@ With the help of this powerful result, we proceed in two steps: 2. Compute household-specific policies and the Gorman sharing rule. -For the special case in Section 12.6 of {cite:t}`HansenSargent2013`, where preference shocks are inactive, we can also +For a special case described in Section 12.6 of {cite:t}`HansenSargent2013`, where preference shocks are inactive, we can also + +3. Implement the Arrow-Debreu allocation by allowing households to trade a risk-free one-period bond and a single mutual fund that owns all of the economy's physical capital. + +We shall provide examples of these steps in economies with two and then with many households. -3. Implement the Arrow-Debreu allocation using only a mutual fund (aggregate stock) and a one-period bond. +Before studying these things in the context of the DLE class, we'll first introduce Gorman aggregation in a static economy. -We shall provide examples of these steps in economies with two and many households. -Before studying these things in the context of the DLE class, we first introduce Gorman aggregation in a static economy. + +In addition to what's in Anaconda, this lecture uses the `quantecon` library + +```{code-cell} ipython3 +:tags: [hide-output] + +!pip install --upgrade quantecon +``` + +We make the following imports + +```{code-cell} ipython3 +import numpy as np +from scipy.linalg import solve_discrete_are +from quantecon import DLE +from quantecon import LQ +import matplotlib.pyplot as plt +``` + (static_gorman)= ### Gorman aggregation in a static economy -To see where the sharing rule comes from, start with a static economy with $n$ goods, price vector $p$, and households $j = 1, \ldots, J$. +To indicate where our competitive equilibrium sharing rule originates, we start with a static economy with $n$ goods, price vector $p$, and households $j = 1, \ldots, J$. Let $c^a$ denote the aggregate amount of consumption to be allocated among households. @@ -425,7 +433,7 @@ All households observe the same aggregate information set $\{\mathcal{J}_t\}$; i These restrictions enable Gorman aggregation by ensuring that household demands are affine in wealth. -This will allow us to solve for aggregate allocations and prices without knowing the distribution of wealth across households as we shall see in {ref}`sharing_rules`. +This will allow us to solve for aggregate allocations and prices without knowing the distribution of wealth across households, as we shall see in {ref}`sharing_rules`. ### The representative agent problem @@ -440,11 +448,13 @@ $$ (eq:agg_preference_aggregates) Aggregates are economy-wide totals: $c_t := \sum_j c_{jt}$, $b_t := \sum_j b_{jt}$, $d_t := \sum_j d_{jt}$, and similarly for $(i_t, k_t, h_t, s_t, g_t)$. -Under the Gorman/LQ restrictions, we can compute equilibrium prices and aggregate quantities by synthesizing a fictitious *representative agent* whose first-order conditions reproduce the competitive equilibrium conditions for aggregates. +Under the Gorman/LQ restrictions, we can compute equilibrium prices and aggregate quantities by synthesizing a fictitious *representative agent* whose first-order conditions generate equations that determine correct equilibrium prices and a correct *aggregate* consumption allocation and aggregate capital process. + +* these equations will not be sufficient to determine the allocation of aggregate consumption among individual households. -This representative problem is an aggregation device: it is chosen to deliver the correct *aggregate* allocation and prices. +Posing a social planning problem for a representative problem is thus a device for computing the correct *aggregate* allocation along with correct competitive equilibrium prices. -The representative agent maximizes +The social planner in our representative agent economy maximizes $$ -\frac{1}{2} \mathbb{E}_0 \sum_{t=0}^\infty \beta^t @@ -909,7 +919,7 @@ def heter( -This section studies the special case from Section 12.6 of {cite:t}`HansenSargent2013` in which the Arrow-Debreu allocation can be implemented by opening competitive markets only in a mutual fund and a one-period bond. +This section studies a special case from Section 12.6 of {cite:t}`HansenSargent2013` in which the Arrow-Debreu allocation can be implemented by opening competitive markets only in a mutual fund and a one-period bond. * So in our setting, we don't literally require that markets in a complete set of contingent claims be present. @@ -995,7 +1005,7 @@ First, write the budget constraint. Household $j$'s time-$t$ resources are its mutual fund dividend $\mu_j d_t$ plus the return on its bond position $R(\mu_j k_{t-1} + \hat{k}_{j,t-1})$. -Its uses of funds are consumption $c_{jt}$ plus the new bond position $(\mu_j k_t + \hat{k}_{jt})$: +Its uses of funds are consumption $c_{jt}$ plus the new bond position $(\mu_j k_t + \hat{k}_{jt})$, so $$ \underbrace{\mu_j d_t}_{\text{mutual fund dividend}} + \underbrace{R(\mu_j k_{t-1} + \hat{k}_{j,t-1})}_{\text{bond return}} = \underbrace{c_{jt}}_{\text{consumption}} + \underbrace{(\mu_j k_t + \hat{k}_{jt})}_{\text{new bonds}} @@ -1401,7 +1411,7 @@ The next figure plots the limited-markets bond adjustment and confirms that the --- mystnb: figure: - caption: bond adjustment positions + caption: bond position adjustments name: fig-gorman-bond-adjustment --- fig, ax = plt.subplots() @@ -1755,9 +1765,7 @@ plt.show() ### Closed-loop state-space system -The DLE framework represents the economy as a linear state-space system. - -After solving the optimal control problem and substituting the policy rule, we obtain a closed-loop system: +We can use our DLE tools first to solve the representative-household social planning problem and represent equilibrium quantities as a linear closed-loop state-space system that takes the usual Chapter 5 {cite:t}`HansenSargent2013` form $$ x_{t+1} = A_0 x_t + C w_{t+1}, @@ -1774,7 +1782,7 @@ z_t \end{bmatrix}, $$ -with $h_{t-1}$ the household service stock, $k_{t-1}$ the capital stock, and $z_t$ the exogenous state (constant, aggregate endowment states, and idiosyncratic shock states). +with $h_{t-1}$ the aggregate household service stock, $k_{t-1}$ the aggregate capital stock, and $z_t$ the exogenous state (constant, aggregate endowment states, and idiosyncratic shock states). Any equilibrium quantity is a linear function of the state. @@ -1816,7 +1824,7 @@ n_k = np.atleast_2d(econ.thetak).shape[0] n_endo = n_h + n_k # endogenous state dimension ``` -With the state-space representation, we can compute impulse responses to show how shocks propagate through the economy. +With the state-space representation, we can compute impulse responses to show how shocks propagate. To trace the impulse response to shock $j$, we set `shock_idx=j` which selects column $j$ of the loading matrix $C$. @@ -1927,12 +1935,11 @@ Indeed, the average of individual household endowments tracks the aggregate endo ## Redistributing by adjusting Pareto weights -This section analyzes Pareto-efficient tax-and-transfer schemes by simply taking competitive equilibrium allocations and then using +This section analyzes Pareto-efficient tax-and-transfer schemes by starting with competitive equilibrium allocations and using a specific a set of nonnegative Pareto weights that sum to one. ```{note} -There are various schemes that would deliver such efficient redistributions, but in terms of what interests us in this example, -they are all equivalent. +There are various tax-and-transfer schemes that would deliver such efficient redistributions, but in terms of what interests us in this example, they are all equivalent. ``` ### Redistribution via Pareto weights From 41ce306d70ca34a2836f3237c33f24e220149bbb Mon Sep 17 00:00:00 2001 From: Humphrey Yang Date: Sat, 27 Dec 2025 21:23:39 +1100 Subject: [PATCH 15/17] updates --- lectures/gorman_heterogeneous_households.md | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/lectures/gorman_heterogeneous_households.md b/lectures/gorman_heterogeneous_households.md index 4a05ee70..858bd8e2 100644 --- a/lectures/gorman_heterogeneous_households.md +++ b/lectures/gorman_heterogeneous_households.md @@ -27,7 +27,7 @@ kernelspec: ## Overview -{cite:t}`gorman1953community` described a class of environments models with preferences having the useful property that there exists a "representative household" in the sense that competitive equilibrium allocations can be computed in the following way: +{cite:t}`gorman1953community` described a class of models with preferences having the useful property that there exists a "representative household" in the sense that competitive equilibrium allocations can be computed in the following way: * take the heterogeneous preferences of a diverse collection of households and from them synthesize the preferences of a single hypothetical "representative household" * collect the endowments of all households and give them to the representative household @@ -63,13 +63,13 @@ After that, we can use some of our DLE tools to compute competitive equilibrium * prices without knowing the allocation of consumption to individual households * households' individual wealth levels * households' consumption levels -* + Thus, this lecture builds on tools and Python code described in {doc}`hs_recursive_models`, {doc}`growth_in_dles`, and {doc}`irfs_in_hall_model`. -In a little more detail, when conditions for Gorman aggregation of preferences are satisfied, we can compute a competitive equilibrium of heterogeneous-household economy in two steps: solve a representative-agent linear-quadratic planning problem for aggregates, then recover household allocations via a sharing-formula that makes each household's consumption a household-specific constant share of aggregate consumption. +In a little more detail, when conditions for Gorman aggregation of preferences are satisfied, we can compute a competitive equilibrium of a heterogeneous-household economy in two steps: solve a representative-agent linear-quadratic planning problem for aggregates, then recover household allocations via a sharing-formula that makes each household's consumption a household-specific constant share of aggregate consumption. * a household's share parameter will depend on the implicit Pareto weight implied by the initial distribution of endowment. @@ -452,7 +452,7 @@ Under the Gorman/LQ restrictions, we can compute equilibrium prices and aggregat * these equations will not be sufficient to determine the allocation of aggregate consumption among individual households. -Posing a social planning problem for a representative problem is thus a device for computing the correct *aggregate* allocation along with correct competitive equilibrium prices. +Posing a social planning problem for a representative agent is thus a device for computing the correct *aggregate* allocation along with correct competitive equilibrium prices. The social planner in our representative agent economy maximizes @@ -1912,8 +1912,8 @@ fig, axes = plt.subplots(2, 1, figsize=(14, 8)) # Top panel: Aggregate endowment axes[0].plot(time_idx, d_agg[:T_plot], linewidth=2.5, color='C0', label='Aggregate endowment $d_t$') -axes[0].set_ylabel('Endowment') axes[0].set_title('Aggregate Endowment') +axes[0].set_ylabel('endowment') axes[0].legend() # Also plot the mean across households @@ -1921,9 +1921,9 @@ d_mean = d_households[:, :T_plot].mean(axis=0) axes[1].plot(time_idx, d_mean, linewidth=2.5, color='black', linestyle='--', label=f'Mean across {d_households.shape[0]} households', alpha=0.8) -axes[1].set_xlabel('Time (after burn-in)') -axes[1].set_ylabel('Endowment') axes[1].set_title(f'Average of Individual Household Endowments') +axes[1].set_xlabel('time (after burn-in)') +axes[1].set_ylabel('endowment') axes[1].legend(loc='upper right', ncol=2) plt.tight_layout() @@ -1935,8 +1935,7 @@ Indeed, the average of individual household endowments tracks the aggregate endo ## Redistributing by adjusting Pareto weights -This section analyzes Pareto-efficient tax-and-transfer schemes by starting with competitive equilibrium allocations and using a specific -a set of nonnegative Pareto weights that sum to one. +This section analyzes Pareto-efficient tax-and-transfer schemes by starting with competitive equilibrium allocations and using a specific set of nonnegative Pareto weights that sum to one. ```{note} There are various tax-and-transfer schemes that would deliver such efficient redistributions, but in terms of what interests us in this example, they are all equivalent. From 54e660cb3972ba8fb671abf7d708b489dcd1658b Mon Sep 17 00:00:00 2001 From: Humphrey Yang Date: Sun, 28 Dec 2025 00:09:29 +1100 Subject: [PATCH 16/17] add more burnin period --- lectures/gorman_heterogeneous_households.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lectures/gorman_heterogeneous_households.md b/lectures/gorman_heterogeneous_households.md index 858bd8e2..9b91af01 100644 --- a/lectures/gorman_heterogeneous_households.md +++ b/lectures/gorman_heterogeneous_households.md @@ -1395,10 +1395,12 @@ mystnb: name: fig-gorman-consumption --- T_plot = 250 +t0 = 200 + fig, ax = plt.subplots() -ax.plot(paths["c"][0, 5:T_plot], lw=2, label="aggregate") -ax.plot(paths["c_j"][0, 5:T_plot], lw=2, label="household 1") -ax.plot(paths["c_j"][1, 5:T_plot], lw=2, label="household 2") +ax.plot(paths["c"][0, t0:t0+T_plot], lw=2, label="aggregate") +ax.plot(paths["c_j"][0, t0:t0+T_plot], lw=2, label="household 1") +ax.plot(paths["c_j"][1, t0:t0+T_plot], lw=2, label="household 2") ax.set_xlabel("time") ax.set_ylabel("consumption") ax.legend() @@ -1415,9 +1417,9 @@ mystnb: name: fig-gorman-bond-adjustment --- fig, ax = plt.subplots() -ax.plot(paths["k_hat"][0, 5:T_plot], lw=2, label="household 1") -ax.plot(paths["k_hat"][1, 5:T_plot], lw=2, label="household 2") -ax.plot(paths["k_hat"][:, 5:T_plot].sum(axis=0), lw=2, label="sum") +ax.plot(paths["k_hat"][0, t0:t0+T_plot], lw=2, label="household 1") +ax.plot(paths["k_hat"][1, t0:t0+T_plot], lw=2, label="household 2") +ax.plot(paths["k_hat"][:, t0:t0+T_plot].sum(axis=0), lw=2, label="sum") ax.axhline(0.0, color="k", lw=1, alpha=0.5) ax.set_xlabel("time") ax.set_ylabel("bond position") @@ -1707,8 +1709,6 @@ b_bar = 5.0 γs_pref = np.zeros(N) ρ_pref = 0.0 -t0 = 200 - A22, C2, Ub, Ud, Ub_list, Ud_list, x0 = build_gorman_extended( n=N, rho1=ρ1, rho2=ρ2, sigma_a=σ_a, From 39336cf7b4884d868457e957d34f2e2bb11ab1e8 Mon Sep 17 00:00:00 2001 From: Humphrey Yang Date: Sun, 28 Dec 2025 00:16:24 +1100 Subject: [PATCH 17/17] add seed to code --- lectures/gorman_heterogeneous_households.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/lectures/gorman_heterogeneous_households.md b/lectures/gorman_heterogeneous_households.md index 9b91af01..468fce38 100644 --- a/lectures/gorman_heterogeneous_households.md +++ b/lectures/gorman_heterogeneous_households.md @@ -1254,7 +1254,8 @@ Using the `LQ` class, we solve the LQ problem and simulate paths for the full st Finally, we call `compute_household_paths` to get household allocations and limited-markets portfolios along the simulated path ```{code-cell} ipython3 -def solve_model(info, tech, pref, U_b_list, U_d_list, γ_1, Λ, z0, ts_length=2000): +def solve_model(info, tech, pref, U_b_list, U_d_list, γ_1, Λ, + z0, ts_length=2000, seed=1): """ Solve the representative-agent DLE problem and compute household paths. """ @@ -1269,7 +1270,8 @@ def solve_model(info, tech, pref, U_b_list, U_d_list, γ_1, Λ, z0, ts_length=20 # Solve LQ problem and simulate paths lq = LQ(econ.Q, econ.R, econ.A, econ.B, econ.C, N=econ.W, beta=econ.beta) - x_path, _, _ = lq.compute_sequence(x0_full, ts_length=ts_length) + x_path, _, _ = lq.compute_sequence(x0_full, + ts_length=ts_length, random_state=seed) paths = compute_household_paths( econ=econ, @@ -1372,7 +1374,8 @@ ts_length = 2_000 # Solve LQ problem and simulate paths lq = LQ(econ.Q, econ.R, econ.A, econ.B, econ.C, N=econ.W, beta=econ.beta) -x_path, _, _ = lq.compute_sequence(x0, ts_length=ts_length) +x_path, _, _ = lq.compute_sequence(x0, + ts_length=ts_length, random_state=1) paths = compute_household_paths( econ=econ, @@ -1395,7 +1398,7 @@ mystnb: name: fig-gorman-consumption --- T_plot = 250 -t0 = 200 +t0 = 200 fig, ax = plt.subplots() ax.plot(paths["c"][0, t0:t0+T_plot], lw=2, label="aggregate")