Models¶
This section contains the API documentation for economic model classes and building blocks.
Core Model Classes¶
DBlock¶
- class skagent.model.DBlock(name='', description='', shocks=<factory>, dynamics=<factory>, reward=<factory>)¶
Bases:
Block
Represents a ‘block’ of model behavior. It prioritizes a representation of the dynamics of the block. Control variables are designated by the appropriate dynamic rule.
- Parameters:
shocks (Mapping(str, Distribution or tuple)) –
A mapping from variable names to Distribution objects, representing exogenous shocks.
Optionally, the mapping can be to tuples of Distribution constructors and dictionary of input arguments. In this case, the dictionary can map argument names to numbers, or to strings. The strings are parsed as mathematical expressions and evaluated in the scope of a calibration dictionary.
dynamics (Mapping(str, str or callable)) – A dictionary mapping variable names to mathematical expressions. These expressions can be simple functions, in which case the argument names should match the variable inputs. Or these can be strings, which are parsed into functions.
reward (Mapping(str, str)) – A dictionary mapping variable names to agent role labels. The variable name will almost always appear in ‘dynamics’. The agent role indicates which agent views the variable as a reward to optimize.
name (
str
)description (
str
)
- calc_reward(vals)¶
Computes the reward for a given set of variable values
- construct_shocks(calibration, rng=None)¶
Constructs all shocks given calibration. This method mutates the DBlock.
- Parameters:
calibration (dict) – Calibration parameters for shock construction
rng (np.random.Generator, optional) – Random number generator to use for distribution construction
- deep_replace(name=None, description=None, shocks=None, dynamics=None, reward=None)¶
Creates a deep copy of the block with new shocks, dynamics, and rewards dictionaries. These dictionaries will have updated values based on the inputs.
- discretize(disc_params)¶
Returns a new DBlock which is a copy of this one, but with shock discretized.
- get_arrival_value_function(disc_params, dr, continuation)¶
Returns an arrival value function, which is the value of the states upon arrival into the block.
This involves taking an expectation over shocks (which must first be discretized), a decision rule, and a continuation value function.)
- get_decision_value_function(dr, continuation)¶
Given a decision rule and a continuation value function, return a function for the value at the decision step/tac, after the shock have been realized.
## TODO: it would be better to systematize these value functions per block ## better, then construct them with ‘partial’ methods
- get_dynamics()¶
- get_shocks()¶
- get_state_rule_value_function_from_continuation(continuation, screen=False)¶
Given a continuation value function, returns a state-rule value function: the value for each state and decision rule. This value includes both the reward for executing the rule ‘this period’, and the continuation value of the resulting states.
- get_vars()¶
Returns the variables that are created/modified by the Block. Does not include variables that are only used as arguments to the dynamics. TODO: Get a way to find these.
- iter_dblocks()¶
A DBlock is its own leaf.
- transition(pre, dr, screen=False, until=None, fix=None)¶
RBlock¶
- class skagent.model.RBlock(name='', description='', blocks=<factory>)¶
Bases:
Block
A recursive block.
- construct_shocks(calibration, rng=None)¶
Construct all shocks given a calibration dictionary.
- Parameters:
calibration (dict) – Calibration parameters for shock construction
rng (np.random.Generator, optional) – Random number generator to use for distribution construction
- discretize(disc_params)¶
Recursively discretizes all the blocks. It replaces any DBlocks with new blocks with discretized shocks.
- get_controls()¶
Returns only the Control variables from the Block dynamics.
- get_dynamics()¶
- get_shocks()¶
- get_vars()¶
- iter_dblocks()¶
Iterate over all DBlock leaves in this RBlock tree.
- property reward¶
The reward attributions for all subblocks.
Control¶
- class skagent.model.Control(iset, lower_bound=None, upper_bound=None, agent=None)¶
Bases:
object
Used to designate a variable that is a control variable.
- Parameters:
iset (list of str) – The labels of the variables that are in the information set of this control.
lower_bound (function) – An ‘equation function’ which evaluates to the lower bound of the control variable.
upper_bound (function) – An ‘equation function’ which evaluates to the upper bound of the control variable.
agent (str) – A label identifying the agent role to which this control is attributed.
Aggregate¶
Model Utilities¶
Simulation Dynamics¶
- skagent.model.simulate_dynamics(dynamics, pre, dr)¶
From the beginning-of-period state (pre), follow the dynamics, including any decision rules, to compute the end-of-period state.
- Parameters:
dynamics (Mapping[str, Callable]) – Maps variable names to functions from variables to values. Can include Controls ## TODO: Make collection of equations into a named type
- preMapping[str, Any]
Bound values for all variables that must be known before beginning the period’s dynamics.
- drMapping[str, Callable]
Decision rules for all the Control variables in the dynamics.
Shock Construction¶
- skagent.model.construct_shocks(shock_data, scope, rng=None)¶
Returns a dictionary from shock labels to Distributions.
When the corresponding value in shock_data contains a distribution constructor and input information, any symbolic expressions used in the inputs are evaluated in the provided scope.
- Parameters:
shock_data (Mapping(str, Distribution or tuple)) –
A mapping from variable names to Distribution objects, representing exogenous shocks.
Optionally, the mapping can be to tuples of Distribution constructors and dictionary of input arguments. In this case, the dictionary can map argument names to numbers, or to strings. The strings are parsed as mathematical expressions and evaluated in the scope of a calibration dictionary.
- scope: dict(str, values)
Variables assigned to numerical values. The scope in which expressions will be evaluated
- rng: np.random.Generator, optional
Random number generator to pass to distribution constructors. If provided, distributions created from tuples will use this RNG.
Discretized Shock Distribution¶
- skagent.model.discretized_shock_dstn(shocks, disc_params)¶
Discretizes a collection of independent shocks and combines them into one DiscreteDistributionLabeled.
Shocks are discretized only if they have a corresponding element of disc_params defined.
Predefined Models¶
The skagent.models
subpackage contains predefined economic models:
Consumer Models¶
Benchmarks¶
Complete Catalogue of Analytically Solvable Consumption-Savings Models
This module implements the exhaustive collection of discrete-time consumption-savings dynamic programming problems for which the literature has succeeded in writing down true closed-form policies. The catalogue is complete as of June 13, 2025, to the best of our knowledge: no other analytically solvable DP variants are currently known.
THEORETICAL FOUNDATION¶
An entry qualifies for inclusion ONLY if: (i) The problem is a bona-fide dynamic programming problem (ii) The optimal c_t (and any other control) can be written in closed form with no recursive objects left implicit
Global Notation (Discrete Time Only)¶
t ∈ {0,1,2,…} : period index A_t : end-of-period assets (risk-free bond, gross return R=1+r>1) y_t : non-capital income c_t : consumption m_t := A_t + h_t : cash-on-hand; h_t = E_t[∑_{s≥0} R^{-(s+1)} y_{t+s}] u(c) : period utility TVC : lim_{T→∞} E_0[β^T u’(c_T) A_T] = 0
- class skagent.models.benchmarks.U6HabitSolver(calibration)¶
Exact Riccati solution for quadratic utility with habit formation
Note: The current implementation uses a simplified solution that is exact for the standard calibration (quad_a=1.0, quad_b=0.5). For general parameters, a full 2×2 algebraic Riccati equation solver would be needed.
- skagent.models.benchmarks.d1_analytical_lifetime_reward(initial_wealth, discount_factor, interest_rate)¶
Analytical lifetime reward for D-1: Two-period log utility model.
With optimal policy c1 = W/(1+β), c2 = βRW/(1+β): Lifetime reward = ln(c1) + β*ln(c2) = ln(W/(1+β)) + β*ln(βRW/(1+β)) = (1+β)*ln(W) + β*ln(βR) - (1+β)*ln(1+β)
- skagent.models.benchmarks.d1_analytical_policy(calibration)¶
D-1: c1 = W/(1+β), c2 = β*R*W/(1+β)
- skagent.models.benchmarks.d2_analytical_lifetime_reward(initial_wealth, discount_factor, interest_rate, time_horizon)¶
Analytical lifetime reward for D-2: Finite horizon log utility.
Forward simulation that exactly matches the D-2 model implementation.
- skagent.models.benchmarks.d2_analytical_policy(calibration)¶
D-2: c_t = (1-β)/(1-β^(T-t+1)) * W_t (remaining horizon formula)
- skagent.models.benchmarks.d3_analytical_lifetime_reward(cash_on_hand, discount_factor, interest_rate, risk_aversion)¶
Analytical lifetime reward for D-3: Infinite horizon CRRA.
With optimal policy c = κ*m where κ = (R - (βR)^(1/σ))/R: V(m) = κ^(1-σ)/(1-σ) * m^(1-σ) / (1 - β*(βR)^((1-σ)/σ))
- skagent.models.benchmarks.d3_analytical_policy(calibration)¶
D-3: c_t = κ*m_t where κ = (R - (βR)^(1/σ))/R
- skagent.models.benchmarks.d4_analytical_policy(calibration)¶
D-4: c_t = κ_s*m_t where κ_s = 1 - (sβR)^(1/σ)/R
- skagent.models.benchmarks.euler_equation_test(model_id, test_points=100)¶
Test Euler equation satisfaction for stochastic analytical solutions
- skagent.models.benchmarks.get_analytical_lifetime_reward(model_id, *args, **kwargs)¶
Get analytical lifetime reward for a benchmark model.
- skagent.models.benchmarks.get_analytical_policy(model_id)¶
Get analytical policy function by model ID
- skagent.models.benchmarks.get_benchmark_calibration(model_id)¶
Get benchmark calibration by model ID
- skagent.models.benchmarks.get_benchmark_model(model_id)¶
Get benchmark model by ID (D-1, D-2, D-3, D-4, U-1, U-2, U-3, U-4, U-5, U-6)
- skagent.models.benchmarks.get_custom_validation(model_id)¶
Get custom validation function for model (if it has one)
- skagent.models.benchmarks.get_test_states(model_id, test_points=10)¶
Get test states for model validation by model ID
- skagent.models.benchmarks.list_benchmark_models()¶
List all 10 analytically solvable discrete-time models from the catalogue
- skagent.models.benchmarks.u1_analytical_policy(calibration)¶
U-1: Martingale consumption c_t = c_{t-1} when β*R = 1
- skagent.models.benchmarks.u2_analytical_policy(calibration)¶
U-2: c_t = (r/R)*A_t + y_t - (1/r)*[log(βR)/γ + γ*σ²/2]
- skagent.models.benchmarks.u3_analytical_policy(calibration)¶
U-3: Martingale consumption with time-varying β_t = 1/R_t
- skagent.models.benchmarks.u4_analytical_policy(calibration)¶
U-4: c_t = (1-β)*[A_t + H_t] where H_t is human wealth
- skagent.models.benchmarks.u5_analytical_policy(calibration)¶
U-5: Collapses to D-3 CRRA rule when θ=γ
- skagent.models.benchmarks.u6_analytical_policy(calibration)¶
U-6: Linear state-feedback c_t = φ1*y_t + φ2*h_{t-1} (exact Riccati solution)
Perfect Foresight Models¶
This page is under construction. Content will be added as the API develops.