Coverage for HARK / ConsumptionSaving / ConsAggIndMarkovModel.py: 43%
46 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-26 06:00 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-26 06:00 +0000
1"""
2General-purpose consumer type with combined aggregate and idiosyncratic
3discrete Markov states — the "hierarchical Markov" pattern.
5Both Krusell-Smith (1998) and the HAFiscal aggregate-demand model share
6a common structure: agents face discrete *macro* (aggregate) states that
7are common to the whole economy, plus discrete *micro* (idiosyncratic)
8states whose transition probabilities depend on the current macro state.
9The full state space is the Cartesian product of the two, encoded as a
10single integer index:
12 combined = num_micro_states * macro_state + micro_state
14This module provides:
16* ``make_hierarchical_mrkv_array`` — builds the full (M*N) x (M*N) Markov
17 transition matrix from an M x M aggregate matrix and M conditional N x N
18 micro matrices.
20* ``AggIndMarkovConsumerType`` — an ``AgentType`` subclass that implements
21 two-step Markov draws (macro from economy, micro per-agent) each period.
23Design note
24-----------
25This class was created in response to the prompt:
27 "Create a comprehensive, self-contained prompt document that will guide
28 creation of a new general-purpose HARK class (AggIndMarkovConsumerType)
29 and companion AggIndMarkovEconomy that unify the patterns currently
30 implemented ad-hoc in HARK's KrusellSmithType and HAFiscal's
31 AggFiscalType."
33The prompt was executed by Claude Opus 4.6 (Anthropic, 2025).
34"""
36import numpy as np
38from HARK import AgentType
41# =============================================================================
42# Utility: build the full hierarchical Markov transition matrix
43# =============================================================================
46def make_hierarchical_mrkv_array(MacroMrkvArray, CondMrkvArrays):
47 """
48 Build a full (M*N) x (M*N) Markov transition matrix.
50 Parameters
51 ----------
52 MacroMrkvArray : np.ndarray, shape (M, M)
53 Aggregate Markov transition matrix.
54 CondMrkvArrays : list of np.ndarray, each shape (N, N)
55 ``CondMrkvArrays[j][mi, mj]`` is ``Pr(micro'=mj | micro=mi, macro'=j)``.
56 There must be M such matrices (one per destination macro state).
58 Returns
59 -------
60 np.ndarray, shape (M*N, M*N)
61 Full transition matrix with combined-state indexing
62 ``combined = N * macro + micro``.
63 """
64 M = MacroMrkvArray.shape[0]
65 N = CondMrkvArrays[0].shape[0]
66 full_size = M * N
67 MrkvArray = np.zeros((full_size, full_size))
69 for i in range(M):
70 for j in range(M):
71 p_macro = MacroMrkvArray[i, j]
72 cond_micro = CondMrkvArrays[j]
73 MrkvArray[N * i : N * (i + 1), N * j : N * (j + 1)] = p_macro * cond_micro
75 return MrkvArray
78# =============================================================================
79# AggIndMarkovConsumerType
80# =============================================================================
83class AggIndMarkovConsumerType(AgentType):
84 """
85 A consumer with two-level hierarchical discrete Markov states.
87 * **M** aggregate (macro) states — common to all agents, received from
88 an economy each period via the sow variable ``"Mrkv"``.
89 * **N** idiosyncratic (micro) states — drawn per-agent each period,
90 conditional on the new macro state.
92 The combined state index is ``N * macro + micro``.
94 This class does **not** bundle a solver; subclasses must set one
95 via ``default_["solver"]``.
97 Subclass hooks
98 --------------
99 * ``get_micro_markov_states()`` — override for exact-match or other
100 custom micro-state draws (default: stochastic draw from
101 ``CondMrkvArrays``).
102 * ``get_states()``, ``get_controls()``, ``get_poststates()`` — the
103 usual model-specific economics.
105 Attributes set each period
106 --------------------------
107 * ``MacroMrkvNow`` (int): current macro state (scalar, from economy).
108 * ``MicroMrkvNow`` (np.ndarray of int): per-agent micro states.
109 * ``MrkvCombined`` (np.ndarray of int): per-agent combined-state indices.
110 """
112 shock_vars_ = ["Mrkv"]
114 def __init__(self, num_macro_states, num_micro_states, **kwds):
115 self.num_macro_states = num_macro_states
116 self.num_micro_states = num_micro_states
117 self.CondMrkvArrays = None # set by economy or subclass
118 AgentType.__init__(self, **kwds)
120 # ----- Hierarchical Markov draw machinery --------------------------------
122 def get_markov_states(self):
123 """Two-step draw: macro (from economy), then micro (per-agent)."""
124 self.get_macro_markov_states()
125 self.get_micro_markov_states()
126 N = self.num_micro_states
127 self.MrkvCombined = N * self.MacroMrkvNow + self.MicroMrkvNow
129 def get_macro_markov_states(self):
130 """Read the scalar macro state sowed by the economy as ``"Mrkv"``."""
131 self.MacroMrkvNow = int(self.shocks["Mrkv"])
133 def get_micro_markov_states(self):
134 """
135 Draw idiosyncratic micro states conditional on the current macro state.
137 Default implementation: stochastic draw from ``self.CondMrkvArrays``.
138 Override for exact-match permutation logic (e.g. Krusell-Smith).
139 """
140 N = self.num_micro_states
141 MacroNow = self.MacroMrkvNow
142 micro_prev = self.MicroMrkvNow.copy()
144 new_micro = np.empty_like(micro_prev)
145 for mi in range(N):
146 mask = micro_prev == mi
147 n_agents = mask.sum()
148 if n_agents == 0:
149 continue
150 probs = self.CondMrkvArrays[MacroNow][mi, :]
151 probs = probs / probs.sum()
152 draws = self.RNG.choice(N, size=n_agents, p=probs)
153 new_micro[mask] = draws
154 self.MicroMrkvNow = new_micro
156 # ----- Convenience -------------------------------------------------------
158 def macro_from_combined(self, mrkv):
159 """Extract the macro state from a combined-state index."""
160 return mrkv // self.num_micro_states
162 def micro_from_combined(self, mrkv):
163 """Extract the micro state from a combined-state index."""
164 return mrkv % self.num_micro_states