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.

Constraints and structured Conditions

Most constraints do not need Condition. The choice depends on what pylcm must know in order to express the restriction along the selected solver’s candidate route.

Use an ordinary callable when the Boolean result is enough

import jax.numpy as jnp


def feasible_consumption(*, consumption, resources):
    return (consumption > 0) & (consumption <= resources) & jnp.isfinite(consumption)

GridSearch evaluates this predicate on complete state-action candidates. A specialized solver can also evaluate it at any route stage where every argument exists.

Use Condition when the comparison structure must survive

A callable exposes inputs and an output, but not the fact that it means savings >= 0 or wealth < asset_limit. Retain that structure when a specialized solver must:

You may also choose Condition simply because its declarative form is clearer. It does not expand the mathematical set of Booleans available to grid search, and it does not make an unsupported constraint supported by an EGM solver.

Syntax

ref(name) refers to a state, action, DAG output, parameter, or declared margin role.

import lcm

# Named value and literal.
nonnegative_savings = lcm.ref("savings") >= 0.0

# Named value and named value.
below_asset_limit = lcm.ref("wealth") < lcm.ref("asset_limit")

# Intersection and union.
working_age = (lcm.ref("age") >= 18) & (lcm.ref("age") < 67)
insured_or_eligible = (lcm.ref("insured") == 1) | (
    lcm.ref("income") <= lcm.ref("eligibility_limit")
)

# Complement.
not_retired = ~(lcm.ref("retired") == 1)

# Conditional requirement.
hours_if_working = lcm.implies(
    premise=lcm.ref("working") == 1,
    consequent=lcm.ref("hours") <= 40,
)

Use &, |, and ~, not Python’s and, or, and not. Comparisons may use <, <=, ==, !=, >=, and >. Strictness determines which side owns an exact boundary point.

When a particular solver needs retained structure

RestrictionBroad callable evaluationWhy retained structure may be needed
Consumption is finite and below resourcesComplete grid-search candidateUsually not needed
Savings lower boundSavings may be constructed rather than exposed at every EGM stageSolver proves it from its grid
Liquid-state intervalNBEGM partitions work by declared liquid boundariesCompiler needs names, thresholds, and strictness
Arbitrary Boolean using unavailable intermediate valuesGrid search may expose themStructure cannot help unless the solver implements a proof/compiler

The last row is important: Condition is information, not magic.

Prefer a margin-specific lower bound

For the borrowing constraint of a declared liquid margin, prefer:

borrowing_limit = lcm.post_decision_lower_bound(
    margin=liquid,
    lower=0.0,
)

It prevents the name in the condition from drifting away from the LiquidMargin.post_decision_state name and lets the solver compare the lower bound with its savings grid.

Budget kinks and cliffs are not ordinary feasibility constraints. Declare them with case pieces or a piecewise-affine schedule.