Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Model and Regime

Model

Model(...) assembles regimes over one lifecycle:

import lcm

model = lcm.Model(
    ages=lcm.AgeGrid(start=25, stop=80, step="Y"),
    regimes={"working": working, "retired": retired},
    regime_id_class=RegimeId,
    enable_jit=True,
)

Required arguments are ages, regimes, and a class created with @categorical(ordered=False) whose fields match the regime names. A model must contain at least one non-terminal and one terminal regime.

The mapping-valued slots functions, constraints, states, state_transitions, actions, and derived_categoricals broadcast declarations to regimes. A name is defined at model or regime level, never both. A regime-level None masks a broadcast entry. Broadcast variables unused by every root computation in both phases are pruned; inspect model.pruned_variables.

koopmans_aggregator and certainty_equivalent are single broadcast values. Declare each at model level or in every non-terminal regime, not a mixture. fixed_params binds parameters when the model is built. n_subjects optionally prepares simulation programs for one population shape; parameter shapes and dtypes must remain stable for reuse.

Public inspection attributes include:

model._regimes is private canonical engine state.

Regime

A general regime declares:

FieldContract
transitionRegime transition callable, stochastic declaration, per-target mapping, or None for terminal
activeAge predicate; omitted means always active
states / actionsName-to-grid mappings
functionsNamed DAG functions; a finalized regime needs utility
constraintsOrdinary predicates or structured Condition objects
state_transitionsOrdinary target-state producers for cells not supplied by joint_transitions
joint_transitionsTarget-local shared-draw laws that jointly produce one or more next states
derived_categoricalsDiscrete grids for categorical DAG outputs
solverlcm.solvers.GridSearch() by default
taste_shocksOptional EV1 taste-shock configuration
koopmans_aggregatorOptional regime-level continuation aggregator
certainty_equivalentOptional regime-level lottery reduction
descriptionHuman-readable description

Use Regime.replace(...) to derive a modified immutable declaration.

Terminality is defined by transition is None. Terminal regimes declare no state_transitions, joint_transitions, Koopmans aggregator, or certainty equivalent because they have no continuation.

ConsumptionSavingsRegime and NestedConsumptionSavingsRegime add the economic roles required by EGM-family solvers. See Consumption-saving regimes and margins. Collective fields on Regime are documented separately in Collective regimes.

ExtremeValueTasteShocks

Declare IID extreme-value shocks on the Cartesian product of a regime’s discrete actions with:

regime = Regime(
    ...,
    taste_shocks=lcm.ExtremeValueTasteShocks(),
)

The scale is a strictly positive runtime parameter at params[regime_name]["taste_shocks"]["scale"]. Use no declaration for a hard maximum; zero is not a valid scale.

For each discrete-action combination, the solve first maximizes the feasible Q values over all continuous-action axes. It then applies the EV1 expected-maximum formula to those choice-specific values. Simulation forms the feasible choice-specific values on its policy’s action candidates, adds one independent mean-zero Gumbel shock per discrete-action combination, and takes the argmax. For any fixed candidate values, the expected latent perturbed maximum equals their smoothed log-sum. The shock affects the choice, while simulation publishes the selected unshocked value. DCEGM simulation uses grid-restricted candidates and therefore need not reproduce its off-grid solve value or choice probabilities; see Solvers and capabilities.

This feature requires at least one discrete action and is implemented by GridSearch and DCEGM. It is rejected for NEGM, NBEGM, and NNBEGM; on a collective regime; on a source regime with a ValueDependentTransition; together with a folded IID state (fold=True); and together with a nonlinear certainty equivalent. These are semantic boundaries, not ignored options: the declaration is rejected during Regime declaration or Model construction, before solve.

Derived categoricals

Use derived_categoricals={"name": DiscreteGrid(category_class=Category)} when a parameter is indexed by a categorical function output rather than by a state or action. The function must return an integer code, not a Boolean, because it is used as an array index under JIT.

Parameter ownership

A free function argument becomes a model parameter unless another state, action, DAG function, context value, or fixed parameter supplies it. Values may be given at model, regime, or function level, but each parameter value has one unambiguous source. Start from model.get_params_template() rather than constructing a nested parameter mapping from memory.

Workflow: Defining models and Parameters.