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:

  1. 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.

  2. 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 option solve_shock_horizon controls 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.paradigm solve 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, and KINK_<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 copy AUX_<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);
    

    y has 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:

  1. The regime comes first. The regime-switching machinery selects the regime to simulate from: a stochastic draw from the transition row (simul_stochastic_regimes, default true), 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.

  2. 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_constraints on a model with kinks merge with the auto-generated kink rows – they never overwrite them;

  • a user simul_regime switch 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