A regime is a phase of life with its own utility function, states, actions, and constraints. Models have at least one non-terminal regime and one terminal regime.
For how functions and dependency wiring work inside a regime, see Write Economics, Not Glue Code. This page covers the regime-level features:
Regime anatomy — what each field does
Terminal vs non-terminal regimes
Transitions — regime and state (overview; full reference in Transitions)
The
activepredicate
from lcm import AgeGrid, LogSpacedGrid, Model
from lcm_examples.tiny import (
RegimeId,
retirement,
working_life,
)Regime Anatomy¶
A Regime is defined by these fields:
| Field | Type | Purpose |
|---|---|---|
transition | Callable, MarkovTransition, or None | Next-regime transition function. None marks a terminal regime. Wrap in MarkovTransition for stochastic transitions. |
active | Callable[[float], bool] | Age-based predicate — when the regime is active (default: always). |
states | dict[str, Grid] | State variable grids — pure outcome-space definitions. |
state_transitions | dict[str, Callable | MarkovTransition | None] | How states evolve over time. None for fixed states. |
actions | dict[str, Grid] | Choice variables with grids. |
functions | dict[str, Callable] | Must include "utility"; can include auxiliary functions. |
constraints | dict[str, Callable] | Feasibility constraints on state-action combinations. |
description | str | Optional description string. |
Terminal vs Non-Terminal Regimes¶
Terminal regime:
transition=None. The value function equals the utility function directly — there is no continuation value.Non-terminal regime:
transitionis a callable. pylcm auto-injects an aggregation function that combines utility with the discounted continuation value:
This is the default aggregation. pylcm supports user-defined aggregation functions for non-standard objectives (e.g., Epstein-Zin preferences).
A terminal regime provides the boundary condition for the dynamic programming problem — it is the last period’s value function. Note that the last period can vary across agents, for example through stochastic mortality. A terminal regime does not mean the agent stays in this regime forever (that would be an absorbing regime, which is non-terminal with a self-loop transition).
Common uses:
Simple retirement: one period where the agent consumes all remaining wealth
Death: terminal utility is zero or determined by a bequest function — see the three-regime example in Write Economics
RETIREMENT_AGE = 65
# The retirement regime from lcm_examples.tiny is already a terminal regime
print("Terminal?", retirement.terminal)
print("Utility function:", retirement.functions["utility"].__name__)Terminal? True
Utility function: utility_retirement
Transitions¶
Regimes define two kinds of transitions:
Regime transitions (
transitionfield) — which regime does the agent enter next period? Can be deterministic (return a regime ID) or stochastic (return probabilities viaMarkovTransition).State transitions (
state_transitionsfield) — how do individual state variables evolve? Supports deterministic functions, fixed states (None), stochastic transitions (MarkovTransition), and target-regime-dependent transitions.
See Transitions for the full reference with examples.
The active Predicate¶
The active field is a callable that takes an age (float) and returns True if
the regime can be occupied at that age. This is useful when regimes are only
relevant during certain life stages:
# Working life: active before retirement age
working_life = Regime(
transition=next_regime,
active=lambda age: age < 65,
...
)
# Retirement: active from retirement age onward
retirement = Regime(
transition=None,
active=lambda age: age >= 65,
...
)pylcm uses active to skip regimes that cannot be reached at a given age during
the backward induction. If active is not provided, it defaults to
lambda _age: True (always active).
Note that active does not prevent an agent from entering a regime — it tells
the solver which regimes to compute value functions for at each age. The regime
transition function is what actually determines regime assignment.
Example: Assembling Regimes into a Model¶
A minimal two-regime model to show the full assembly. For the economic functions and DAG wiring, see Write Economics. For model construction details, see Defining Models.
# Customize the imported working_life regime with simpler grids and functions
working_life_simple = working_life.replace(
actions={
**working_life.actions,
"consumption": LogSpacedGrid(start=0.5, stop=50, n_points=50),
},
)
age_grid = AgeGrid(start=25, stop=65, step="20Y")
model = Model(
regimes={
"working_life": working_life_simple,
"retirement": retirement,
},
ages=age_grid,
regime_id_class=RegimeId,
)
print("Regimes:", list(model.regimes.keys()))
print("Periods:", model.n_periods)Regimes: ['working_life', 'retirement']
Periods: 3