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.

Grids

Grids define the outcome space for state and action variables — what values they can take. They are passed via the states and actions mappings on a Regime.

A grid is ordinarily the same for every period the regime is active. For a continuous state, see Age-specialized functions and grids to let the grid’s bounds or node values vary with age instead (e.g., an age-dependent borrowing limit).

Grid breakpoints are numerical, not economic metadata. A piecewise grid places nodes around a known location. It does not tell NBEGM that the budget has a kink, jump, or hard constraint. Declare that structure separately with case pieces or a piecewise-affine schedule.

Quick Reference

Grid TypeUse CaseKey Parameters
DiscreteGridCategorical choicescategory_class
LinSpacedGridEvenly spaced continuousstart, stop, n_points
LogSpacedGridLog-spaced continuousstart, stop, n_points
IrregSpacedGridCustom point placementpoints or n_points
PiecewiseLinSpacedGridDense in some regionsstart, stop, breakpoints, points_per_segment
PiecewiseLogSpacedGridLog-dense in some regionsstart, stop, breakpoints, points_per_segment

All grid classes are imported from lcm:

from lcm import (
    DiscreteGrid,
    LinSpacedGrid,
    LogSpacedGrid,
    IrregSpacedGrid,
    GridBreakpoint,
    PiecewiseLinSpacedGrid,
    PiecewiseLogSpacedGrid,
    categorical,
)

Discrete Grids

DiscreteGrid

For categorical variables. Requires a @categorical frozen dataclass defining the categories:

from lcm import DiscreteGrid, categorical
from lcm.typing import ScalarInt


@categorical(ordered=True)
class LaborSupply:
    do_not_work: ScalarInt
    work: ScalarInt


actions = {"labor_supply": DiscreteGrid(category_class=LaborSupply)}

Values are integer codes (0, 1, 2, ...) auto-assigned by @categorical. In simulation output, labels are preserved via pandas Categorical.

When used as an action, no further configuration is needed. When used as a state, the transition is specified via state_transitions on the Regime — see Transitions.

Continuous Grids

LinSpacedGrid

Evenly spaced points from start to stop (inclusive). The most common grid type for wealth, consumption, and similar variables.

LinSpacedGrid(start=0, stop=100, n_points=50)

LogSpacedGrid

Points concentrated near start (logarithmic spacing). Good for variables with diminishing marginal effects. start must be positive.

LogSpacedGrid(start=0.1, stop=100, n_points=50)

IrregSpacedGrid

Explicit point placement. Use when you need specific grid points (e.g., at policy kinks):

IrregSpacedGrid(points=(0.0, 0.5, 1.0, 5.0, 10.0, 50.0))

You can also defer points to runtime by specifying only n_points. The actual points are then supplied via the params dict:

IrregSpacedGrid(n_points=4)

PiecewiseLinSpacedGrid

Use explicit breakpoints when different parts of one finite domain need different linear resolutions. Each breakpoint declares which neighboring segment owns the exact boundary value:

from lcm import GridBreakpoint, PiecewiseLinSpacedGrid

grid = PiecewiseLinSpacedGrid(
    start=0.0,
    stop=100.0,
    breakpoints=(
        GridBreakpoint(value=10.0, owner="right"),
        GridBreakpoint(value=40.0, owner="left"),
    ),
    points_per_segment=(20, 50, 30),
)

This declaration forms the nominal segments [0, 10), [10, 40], and (40, 100]. The outer endpoints are always included. A right-owned breakpoint is the first node of the segment to its right; a left-owned breakpoint is the last node of the segment to its left. Every breakpoint therefore appears exactly once, and each count is the number of output nodes its segment contributes:

grid.n_points == 20 + 50 + 30

On the open side, the effective endpoint is the representable floating-point value immediately next to the breakpoint. This keeps equality ownership exact without removing a full grid spacing.

Use breakpoints=() with one entry in points_per_segment for a one-segment piecewise declaration. LinSpacedGrid is normally simpler for that case.

PiecewiseLogSpacedGrid

PiecewiseLogSpacedGrid uses the same breakpoint and ownership declarations but logarithmic spacing within each segment. The complete domain and all breakpoints must be positive.

from lcm import GridBreakpoint, PiecewiseLogSpacedGrid

PiecewiseLogSpacedGrid(
    start=0.1,
    stop=1_000.0,
    breakpoints=(GridBreakpoint(value=10.0, owner="right"),),
    points_per_segment=(50, 30),
)

Grid Hierarchy (advanced)

All grids inherit from the Grid base class:

The to_jax() method converts any grid to a JAX array. ContinuousGrid subclasses provide get_coordinate() for mapping values to grid coordinates, used in interpolation.

See Also