1.10. Occasionally-binding constraints
An occasionally-binding constraint (OBC) is an inequality on a model variable that holds with equality only some of the time – the zero (or effective) lower bound on the policy rate, irreversible investment, a collateral constraint, a capacity limit, the irreversibility-of-capital constraint, and so on.
OBCs come in two complementary forms in RISE, and the rest of this chapter is organized around that distinction:
The traditional form: kinky functions in the model equations. Use
min,max,abs, … directly in the right-hand side of the equation that pins the constrained variable. The solver’s treatment of these kinky functions then depends on the context:In a deterministic context (perfect-foresight / extended path), the kinky functions are enforced exactly – the nonlinear deterministic solver treats
max(0, R*)as the true mathematical operation.In a stochastic context (perturbation around a steady state), the kinky functions cannot be enforced exactly in a single linear solution; they are treated slightly differently – the perturbation engine smooths or splits them across regimes, using one of the routes documented below.
The complementary form: regime-switching approximation. The OBC is approximated by a two-state Markov chain (slack regime / binding regime) with the transitions driven by whether the constraint binds. The switching-model solver then handles the problem with the standard regime-switching machinery.
The two forms are not mutually exclusive, in two distinct senses.
First, for a single constraint they are alternative encodings: a
model file with a max(0, ...) kink can be solved
deterministically (exact) or under one of the stochastic-context
treatments (approximate); alternatively, the same constraint can
be recast as a bind Markov chain and solved via the
regime-switching route. Second, one model may carry different
constraints in different forms at the same time – say a ZLB
written as a max kink next to a borrowing limit enforced by
regime switching. Each constraint is then enforced by its own
mechanism; see Mixing the two forms in one model below.
Note
The occbin algorithm is a paradigm. The piecewise-linear OccBin
algorithm lives in the paradigm framework as occbin.paradigm (see
Extending RISE through paradigms), not in
mainland RISE. OBC itself is not a paradigm: the deterministic
(perfect-foresight) and anticipated-shocks routes are ordinary RISE
features. This chapter covers the OBC modeling; the piecewise-linear
solve is delegated to the OccBin paradigm.
1.10.1. Form 1. Kinky functions
Declaration
Write the inequality as a max, min or abs on the
right-hand side of the equation that pins the constrained
variable:
@endogenous PAI Y R RSTAR
@model
RSTAR = rss + phi_pi*PAI; % shadow Taylor rule
R = max(0, RSTAR); % zero lower bound on R
The parser recognizes the kink at parse time. The same model file is used by the deterministic solver and by the stochastic-context routes; no additional declaration is required.
Deterministic context: exact enforcement
For perfect-foresight / extended-path simulations – where the
model is solved as a deterministic two-point boundary problem
over a finite horizon – the kinky functions are enforced
exactly. max(0, RSTAR) is the literal mathematical operation
the deterministic nonlinear solver applies at each period of the
horizon.
This is the route to use for scenarios where the constraint genuinely binds for known stretches of time and the deterministic treatment is appropriate (a known-and-announced rate path, a scheduled fiscal consolidation, etc.). See Deterministic and quasi-deterministic solutions.
Stochastic context: approximate treatments
In a stochastic context the kinky functions cannot be enforced exactly within a single linear (or higher-order) perturbation solution. The solver picks one of the approximate treatments:
Anticipated-shock smoothing. The solver adds one fudge shock per kink-equation (named
FUDGE_FACTOR_n) whose announced future values hold the constrained variable on the admissible side at each iteration. The optionsolve_shock_horizoncontrols how far ahead the fudge shocks are anticipated; the cell-array form sets it per fudge shock for multi-constraint models.Piecewise-linear (OccBin) approximation. Provided by the
occbin.paradigmsolve paradigm – a self-contained piecewise-linear solver that iterates on when the constraint binds across the horizon. See Extending RISE through paradigms.
These are not exact; they are approximations of the kinky constraint inside a stochastic perturbation solver. The choice of treatment is a solve-time option, not a model-file declaration.
Kinks with leads and lags
A kink may sit in any equation, whatever its leads and lags, and its arguments may carry leads and lags themselves:
R = max(0, rho_r*R{-1} + (1-rho_r)*RSTAR); % smoothing inside the ZLB
Y = 0.5*max(Z{+1}, 0.2) + 0.1*E; % a kink dated t+1
The fudge shock of a kink holds it against a check that is evaluated
one period at a time, on the variables dated t. So smooth_kinks
(on by default) gives each outermost max, min or abs of an
equation with a lead or a lag an equation of its own, dated t, and
uses that variable where the kink was:
the kink is moved back to the latest date s among its arguments:
KINK_<eq>_<k>{t}is the kink with every date moved back by s, andKINK_<eq>_<k>{t+s}replaces it in the equation (<eq>is the number of the equation,<k>counts its kinks);arguments dated earlier go through the lag auxiliaries
LAG_<m>_<v>, the ones RISE creates for lags beyond one; a shock goes through its endogenous copyAUX_<x>first;each auxiliary kink has its own
FUDGE_FACTOR_<n>, numbered like its equation. Nested kinks stay inside the auxiliary of the outermost one; a kink of parameters only is a constant and stays in place.
The two equations above (the fourth and the first of their models) become:
R = KINK_4_1; KINK_4_1 = max(0, rho_r*LAG_1_R + (1-rho_r)*RSTAR); LAG_1_R = R{-1}
Y = 0.5*KINK_1_1{+1} + 0.1*E; KINK_1_1 = max(Z, 0.2)
The rewrite is exact, a change of variables. Taking the latest date
keeps the conditional expectation outside the kink, as for any
nonlinear term with a lead: max(Z{+1},0.2) means
E_t max(Z_{t+1}, 0.2), which is E_t KINK_1_1_{t+1}. (A lead auxiliary
inside the max would give max(E_t Z_{t+1}, 0.2), a different
model.) Leads beyond one reach the kink through the auxiliaries that
keep nonlinear terms under a single expectation (JLEAD_<k>).
A kink that enters with a lead sits inside the expectation of date
t, so its fudge shock is anticipated one period by default:
unanticipated, it would enforce the kink only at the date it is
realized, never where it enters the equation. solve_shock_horizon
changes it like any other horizon; a longer horizon checks the kinks
over that stretch of the expected path, and the anticipated values
enter today’s decisions.
The model is displayed as written (equations.dynamic), followed by
the auxiliary equations. The auxiliaries are endogenous variables like
any other and get their steady states automatically: a steady-state
file or @steady_state_model need not mention them. A kinky equation
that dates all its variables at t is left as it is, with its row and
fudge shock as before. Perfect foresight applies the kinks exactly
either way, so it gives the same path with smooth_kinks on and off.
What stays one period is the rows of simul_constraints you declare
yourself: a lead or a lag in them is refused (identifier
RISE:simul_constraints:leadsLags). Declare an auxiliary variable in
the model, e.g. RLAG = R{-1}, and restrict it instead.
Worked example: tutorial ModelShapes/kinks_with_leads_and_lags.
Tests: models/dsge/obc/Test_kinks_with_leads_and_lags.m.
1.10.2. Form 2. Regime-switching approximation
The complementary form recasts the OBC as a two-state Markov chain
whose bind parameter is 0 in the slack regime and 1 in
the binding regime, and writes the constrained equation as the
convex combination of the slack and binding cases:
@parameters(zlb, 2, "slack", "bound") bind
@parameters zlb_tp_1_2 zlb_tp_2_1 i_floor
@model
i_taylor = phi_pi*pi + phi_y*x;
i = bind*i_floor + (1-bind)*i_taylor;
The constraint is now a chain like any other – except that the switching is endogenous (driven by whether the constraint binds), not stochastic.
Solving with the OccBin paradigm
The piecewise-linear solve of this construction is provided by the
occbin.paradigm solve paradigm. Activate it at solve time, naming
the reference (slack) regime and listing the constraints. Each constraint
is written as an intuitive {inequality, chain} pair:
C = { {'R>=1','ocb'} }; % constraint R >= 1 on the 'ocb' chain
m = set(m,'solve_paradigm',{@occbin.paradigm,'ref',1,'constraints',C});
m = solve(m);
sims = simulate(m,'simul_periods',1000);
Here ref is the integer ID of the reference composite regime. In each
pair, the first element is the inequality 'VAR OP BOUND' – OP is one
of >= > <= < and BOUND is a number or a parameter name
(e.g. 'R>=r_zlb') – and the second is the switching chain. >=/>
binds when the variable falls below the bound; <=/< binds when it
rises above. Several constraints are just several pairs:
C = { {'maxlev<=0','debt'}, ... % borrowing limit, 'debt' chain
{'r>=1', 'zlb'} }; % ZLB on the policy rate, 'zlb' chain
The paradigm enforces the constraints at simulation time and scales to any
number of chains. It also imposes a single common steady state and pins the
constraint chains’ transition probabilities to zero itself, so you need not
pass sstate_imposed / sstate_unique or zero those probabilities in
the calibration. The explicit struct form
struct('var','R','dir',-1,'bound',1,'chain','ocb') (dir=-1 ⇔ >=,
dir=+1 ⇔ <=) remains accepted. The constraint-cell grammar and the
remaining options (maxspell, use_pinv) are documented with the
paradigm in Extending RISE through paradigms.
A constrained variable lands on its bound. Every regime is linearized
around the reference steady state. In a regime where constraints bind, the
constant of the linear system is taken where the binding constrained
variables sit on their bounds, so a variable lands exactly on its bound
even when the binding branch is nonlinear in it: a rule written
log(1+R) = gam*log(...), which reads log(1+R) = 0 at the bound, gives
R = 0, as the rule written linear in R does. Equations linear in the
constrained variables keep the constant they have at the steady state, and
the reference regime is unchanged. The option 'anchor_at_bound',false
restores the classical OccBin constant, the residual at the steady state (as
in Guerrieri and Iacoviello’s toolkit), under which such a branch puts the
variable on its tangent at the steady state (R = Rss-(1+Rss)*log(1+Rss),
below the bound) – for reproducing results computed that way. The tutorial
ModelShapes/occbin_bound shows both.
Enforcement at simulation time: switch rules
Outside the OccBin paradigm, the regime-switching construction can be enforced directly by the simulator through regime selection. Two equivalent declarations:
Two-column ``simul_constraints``. Declare the inequality and its recovery value; on a switching model RISE compiles a switch rule from it automatically:
m = set(m,'simul_constraints',{'L<=lbar','L=lbar'});
Each period the simulator forecasts every regime, zeroes the probability of the regimes whose forecasts violate the constraint, and draws the regime from the reweighted transition row.
A user switch rule. Supply the admissibility condition yourself through
simul_regime:rule = @(y,past_regimes,past_forecasts)[y(L_idx,1)<=lbar; true]; m = set(m,'simul_regime',rule);
yhas one column per regime (the candidate forecasts); the rule returns a logical row marking the admissible regimes.
Constraints enforced this way are regime-owned: regime
selection is their enforcement mechanism. This is distinct from
fudge-owned constraints (the kink rows of Form 1, or
simul_constraints supplied with a third column of supporting
shocks), which are enforced by shock adjustment inside the
one-step forecast.
1.10.3. Constraints held by a named shock
The fudge shocks of the kinks are the default way to hold a
constraint: they leave the model’s structural shocks alone. A
simul_constraints row can instead name, in a third column, a
shock of the model that holds the constraint:
m = set(m,'simul_constraints',{'log(1+R)>=0','R=0','ER'});
When the row binds, the named shock keeps its draw and moves only by
what puts the variable on its bound, so that R = max(0, R*),
where R* is the rate the draw alone would give. With anticipated
shocks, each binding period is held by the named shock’s news for
that period, and a period whose bound the later news already lifts is
left free. On a constant-parameter model every row must name its
shock: a row without one is refused
(RISE:simul_constraints:noShock) rather than letting every shock
of the model, structural ones included, hold it.
1.10.4. Mixing the two forms in one model
A model may carry a Form-1 kink (say a ZLB, R = max(0, RSTAR))
and a Form-2 regime-switching constraint (say a borrowing limit
L <= lbar on a fin chain) at the same time. The simulator
enforces each constraint by its own mechanism, in a fixed order
within every simulated period:
The regime comes first. The regime-switching machinery selects the regime to simulate from: a stochastic draw from the transition row (
simul_stochastic_regimes, defaulttrue), or – when a switch rule is active – a draw from the transition row reweighted by the rule, which zeroes the regimes whose candidate forecasts violate the regime-owned constraints.Then the regular one-step forecast runs inside the selected regime, and the fudge-owned constraints are resolved there with fudge shocks – including inside the candidate-regime forecasts that a switch rule compares, and under pre-specified regime paths.
Ownership is per restriction row and unambiguous:
rows auto-generated from kinks are always fudge-owned (they carry their own
FUDGE_FACTOR_*enforcers);user rows are regime-owned when given in 2-column form on a switching model, and fudge-owned when a third column names the shocks that hold them; on a constant-parameter model the third column is required (see Constraints held by a named shock);
user
simul_constraintson a model with kinks merge with the auto-generated kink rows – they never overwrite them;a user
simul_regimeswitch rule is compatible with fudge-owned rows; combining it with regime-owned rows raises an error (two claimants on regime selection).
A minimal worked example (NK model with a max ZLB and a
regime-switching borrowing limit, all three declarations of the
regime-owned constraint) ships with the test suite under
models/dsge/obc/worked_examples/mixed_enforcement.
1.10.5. Side constraints inside an optimization problem
A different but related construction: a planner’s optimization
problem with a side constraint (a \geq / \leq
restriction the planner must honor). The constraint enters
through the @constraint option of an
@optimization_problem block:
@optimization_problem{
@discount = beta,
@objective = -0.5*(pi^2 + lambda_y*y^2),
@constraint = gam : i >= i_floor;
}
The regime indicator gam is a switching parameter that equals
1 when the constraint binds and 0 when slack. RISE
attaches a Lagrange multiplier and writes the
complementary-slackness condition automatically. See
Optimal policy for the full @optimization_problem
grammar.
This is the planner-with-constraint case; it is technically an OBC construction but the analysis discipline is different (the side constraint is part of the policy problem, not a private-sector behavioral equation). Use it when the constraint is a feature of the planner’s choice set; use Form 1 or Form 2 when the constraint is a feature of the equilibrium.
1.10.6. Simulation and IRFs under OBC
Once the constrained model is solved, simulate, irf,
forecast and filter respect the constraint with the
chosen algorithm. Two pieces of guidance:
Use generalized IRFs. With an OBC the model is genuinely nonlinear, so the meaningful impulse response averages over the state distribution. Run
irf(m, irf_type = 'girf', irf_draws = 1000)instead of the simple IRF that traces the response from a fixed point.Watch how often the bound binds. A simulation that spends 100% of periods at the bound (or 0%) is a sign of a calibration outside the determinacy region for the chosen route, not necessarily of a bug. Check the unconstrained-rate distribution against the floor.
Filtered series respect the constraints. The filter applies the constraint engine as the simulation does, in its predictions and in its updates: where the enforcement bends the one-step map, the filter updates the previous state and the period’s shocks through that map (an iterated extended Kalman update, see Filtering), so the updated series respect the constraints by construction and the likelihood accounts for the enforcement. A shadow rate below the floor is inferred from the other observables. Where the data contradict a predicted binding (a rate predicted at its floor and observed above it), the update releases the enforcer and the shadow rate follows the observed rate. Should that update not settle, the linear update is kept and moved back onto the constraints with the least change in the period’s structural shocks, the violated constraints’ own enforcers holding the bounds; the enforcers of every other constraint (in a HANK model, those of the interpolation weights too) stay put. The smoothed series are not adjusted this way: they reproduce the observed data, but in binding periods they need not respect the constraints.
filter(m, simul_disable_constraints = true)filters the model as if it had no constraints (on a constant-parameter model, the plain Kalman filter).
1.10.7. Where to look next
Deterministic and quasi-deterministic solutions – the deterministic context in full; this is where Form 1 delivers exact enforcement.
Extending RISE through paradigms – the
occbin.paradigmengine that performs the piecewise-linear solve of the regime-switching construction.Optimal policy – the
@optimization_problemgrammar, including@constraint.