1.8. Optimal policy

When the model file contains an @optimization_problem block, one or more of the policy equations is replaced by the planner’s first-order conditions. RISE supports:

  • commitment (Ramsey policy), with or without regime switching;

  • discretion, linear-quadratic or nonlinear, with or without regime switching;

  • loose commitment – switching between commitment and discretion with a constant probability, in a linear-quadratic system;

  • stochastic replanning – switching between commitment and discretion, where the replanning state is a regime in its own right, linear-quadratic or nonlinear;

  • non-cooperative games with multiple policymakers, Nash or Stackelberg, with a further choice between open-loop equilibrium (OLE, the historical default) and Markov-perfect equilibrium (MPE) at solve time.

If you instead want to keep all the model equations and only pick the coefficients of a given (e.g. Taylor) rule optimally, see Optimized simple rules in the legacy chapter – the surface is unchanged.

1.8.1. The modern distinctive

In the legacy toolbox, commitment vs discretion was pinned via a @commitment marker inside the @optimization_problem block and via a commitment parameter declared alongside the calibration. The two paths could disagree silently.

In the modern toolbox, the model file declares the game; the solve call selects the equilibrium concept. There is no @commitment marker in the model file, and there is no commitment parameter to set in your calibration (except in the specific loose-commitment / stochastic-replanning cases that genuinely need a switching commitment indicator). The relevant options live on solve:

  • solve_policy_type selects commitment vs discretion;

  • solve_policy_equilibrium selects OLE vs MPE for multi-player games.

The model file does not change when you flip from commitment to discretion. See Modern architecture for the broader pattern.

1.8.2. Declaring the planner’s problem

The grammar is documented in Model file language under Optimal policy. The skeletal forms:

Single player:

@optimization_problem{
    @objective = -0.5*(pi^2 + lambda_y*y^2 + lambda_i*i^2),
    @discount  = beta
}

When the block is present, the model must have strictly fewer equations than endogenous variables. The missing equations are the planner’s first-order conditions, generated by RISE. The instrument is inferred as whichever endogenous variable does not appear on the left-hand side of any equation in @model.

Multi-player Nash:

@optimization_problem[@no_u_turn=false]{
    Monetary:
        @order      = 1
        @discount   = beta
        @objective  = -0.5*(pi^2 + lambda_y*y^2 + lambda_i*i^2)
        @instrument = i ;
    Fiscal:
        @order      = 1
        @discount   = beta
        @objective  = -0.5*(tau^2 + lambda_b*b^2)
        @instrument = tau ;
}

When every player has the same @order, the game is Nash.

Multi-player Stackelberg:

@optimization_problem[@no_u_turn=false]{
    Monetary:
        @order      = 1
        @objective  = ... @instrument = i ;
    Fiscal:
        @order      = 2
        @objective  = ... @instrument = tau ;
}

When the orders differ, the game is a Stackelberg cascade with the lower @order as the leader.

@no_u_turn = true asks RISE to skip the discretion-derivative pipeline in exchange for a cheaper parse. Set it only if you will only ever solve commitment / loose-commitment problems and will not need MPE.

The grammar continues to support the legacy bracket options (@commitment, @markov_process); they remain parseable but the modern convention pushes commitment selection out to the solve call.

1.8.3. Switching parameters on leads and lags

When a switching parameter multiplies a lead or a lag inside a constraint, the generated first-order conditions evaluate it at the regime prevailing at that date – not at the current one.

The reason is mechanical. Differentiating the Lagrangian with respect to \(x_t\) pulls in three blocks: the constraint at \(t\), and two inherited ones,

\[\frac{1}{\beta}\lambda_{t-1}\frac{\partial c(t-1)}{\partial x_t} \qquad\text{and}\qquad \beta\,E_t\left[\lambda_{t+1}\frac{\partial c(t+1)}{\partial x_t}\right]\]

whose coefficients belong to the constraints written at \(t-1\) and \(t+1\). So in a Phillips curve such as:

PAI = omega*PAI(-1) + (1-omega)*betta*PAI(+1) + kappa*Y + U;

with omega declared on a Markov chain, the FOC for PAI carries omega{t-1} in the inherited-past term and omega{t+1} in the forward term, while the contemporaneous term keeps omega. RISE inserts that dating automatically.

Note

Nothing is required of you, and nothing changes for models whose parameters are all constant – dating a constant parameter is a no-op. The distinction only bites when a parameter both switches and sits on a lead or a lag, and it is invisible in a single-regime model, where every date shares the same regime.

The commitment indicator is deliberately not re-dated. It is injected into the constraints before they are differentiated, but its timing is governed by the loose-commitment recursion – \(\mathrm{commitment}_t \cdot \lambda_{t-1}\) asks whether the planner honours an inherited promise in the current period – so it stays at \(t\).

The same facility is available to you directly: a switching parameter may be written omega{t+1} anywhere in a model file, or carried by an auxiliary variable (AUX_omega = omega; and then AUX_omega(+1)). The two are equivalent – see the forward-looking parameters model in the test suite.

1.8.4. Switching parameters in an objective

An @objective is a date-\(t\) object, so a switching parameter inside one is read at the current regime – there is no lead or lag for it to travel to. Loss weights may therefore switch freely, and a policymaker whose preferences are regime-dependent – penalising an instrument more in one regime than in another, or weighting a target differently across regimes – is an ordinary model:

@parameters(sw,2) lambda_y

@optimization_problem{
    @objective   = -0.5*(pi^2 + lambda_y*y^2 + lambda_i*i^2),
    @instrument  = i,
    @discount    = betta
}

This holds for a single planner and for every player of a non-cooperative game, on both the general and the linear-quadratic route.

1.8.5. A lead that a lag multiplies

When a constraint multiplies a lagged variable by a lead, as debt revalued by expected inflation does:

b{t} = b{t-1}/betta*(1+0.1*pie{t+1}) + ... ;

the same constraint one period later holds \(b_t\,\pi_{t+2}\), so the first-order condition for b would carry a lead of two, \(\lambda_{t+1}\pi_{t+2}\). RISE does not let it get there. Before it derives the first-order conditions, it gives the lead its own expectation variable:

LEAD_1_pie{t} = pie{t+1};
b{t} = b{t-1}/betta*(1+0.1*LEAD_1_pie{t}) + ... ;

which is what declaring EPIE{t} = pie{t+1} in the model gives. No first-order condition then carries a lead beyond one, and LEAD_1_pie appears in the solution like any other variable.

The two forms are not equally good to solve. For a single planner facing this debt equation, solved at order 2 under discretion, they have the same solution, but the form with the lead of two takes 756 seconds and the expectation form 2. In the game of a monetary leader and a fiscal follower with the same debt equation (paths of about 0.1), the two forms have the same solution under commitment: their approximations differ only by terms of higher order (\(3\times10^{-8}\) at order 2, \(6\times10^{-11}\) at order 3). Where commitment is 0 – discretion, or the replanning regime of stochastic replanning – they define different equilibria, about \(4\times10^{-6}\) apart under discretion at order 2 and at order 3 alike, and the expectation form solves in 18 seconds against 661. RISE computes the expectation form.

The rewrite applies to terms that are affine in their leads with coefficients known at \(t\) – a lead times lags, current variables and parameters, or times a function of a lag – where it is exact. A term in which a lag and a lead interact nonlinearly, such as b{t-1}*pie{t+1}^2, is left as written, and its lead of two is reduced in the first-order conditions. Lags and leads that only add up, as in pie{t} = beta*pie{t+1} + gam*pie{t-1}, have nothing to separate: a model without a lead that a lag multiplies is untouched.

1.8.6. Equations outside the optimization

Every equation of @model is a constraint of every player. Some equations should not be: a welfare tracker kept for reporting,

W{t} = -0.5*(PI{t}^2 + lam*X{t}^2) + beta*W{t+1};

is solved with the model, expectation included, but the planner is not constrained by it. As a constraint it carries a multiplier that the problem does not pin down, and when the planner discounts like the agents its steady state is a continuum (see The planner-discount knife-edge below). Such equations go in an @outside_optimization block, which lists in braces the variables the players take as given:

@model
    X{t} = X{t+1} - sigma*(R{t} - PI{t+1});
    PI{t} = beta*PI{t+1} + kappa*X{t} + U{t};

@outside_optimization{W}
    "Welfare tracker"
    W{t} = -0.5*(PI{t}^2 + lam*X{t}^2) + beta*W{t+1};

@optimization_problem{@objective = -0.5*(PI^2 + lam*X^2), @discount = beta, @instrument = R}

The equations of the block have the grammar of @model (labels, leads and lags, expectations, nonlinear and implicit forms) and are solved jointly with the model. They carry no multiplier, and the variables in the braces are left out of what the players choose. A block has as many variables in its braces as equations, since leaving k equations out of the constraints leaves k variables out of the choices: the variables need not be paired with equations, only the two sets count. The variables must be endogenous. In a model without an @optimization_problem the block’s equations are ordinary model equations, so the same model file can be solved with and without optimal policy. A block may sit anywhere among the @model blocks, and several blocks are allowed.

With the tracker outside the optimization, the policy problem is the one without the tracker: the multipliers, their steady state and the responses are the same (to rounding error in the tests: commitment and discretion, orders 1 and 2, a single planner, a Nash game and a leader-follower game), and the tracker follows its equation along the solution. In the open-economy model of Bodenstein, Guerrieri and LaBriola (2019), the reporting tracker of home welfare taken outside the optimization gives the paper’s responses at the paper’s calibration, with no multiplier to select.

Multiplier names. LM_<state>_<player>_EQ_<row> keeps naming the model row the multiplier prices, so the numbering has a gap where the block’s equations sit: with the block as the third row and an auxiliary equation after it, the multipliers are LM_1_1_EQ_1, LM_1_1_EQ_2 and LM_1_1_EQ_4.

Feedback. A variable in the braces that also enters a player’s objective or an equation that still constrains it makes leaving the block out change that player’s problem: the player treats the variable as given all the same. RISE says so when the model is read, with the warning RISE:outside_optimization:feedback, or refuses the model with the parse-time option 'outside_optimization_feedback','error'. In a game the warning names each player concerned and its variables, as in player2 (B).

Exempting some players only. In a game, a block may exempt only the players named in parentheses after the keyword; the other players keep its equations as constraints:

@model
    X{t} = rho*X{t-1} + a*X{t+1} + u1{t} + u2{t} + sig*EPS{t};

@outside_optimization(player2){B}
    B{t} = X{t};

@optimization_problem{
    player1:@objective = B{t}^2 + r1*u1{t}^2,
            @instrument = u1, @order = 1, @discount = beta;
    player2:@objective = B{t}^2 + w2*X{t}^2 + r2*u2{t}^2,
            @instrument = u2, @order = 1, @discount = beta
}

Player 2 is not constrained by B = X and takes B as given. In other words, an exempt player never takes derivatives with respect to the exempted variables, and does not include the exempted equations in its system: it has no multiplier on that row and no first-order condition with respect to B, so the B^2 of its objective has no effect on what it does (the feedback warning says so). Player 1 keeps the row and its multiplier, LM_1_1_EQ_2; player 2 has LM_1_2_EQ_1 and no LM_1_2_EQ_2. The game is the one written with B = X in @model and B left out of player 2’s objective; the tests find the same responses under commitment, discretion (open-loop and Markov-perfect) and loose commitment, in a Nash game, with the follower or the leader of a leader-follower game exempt, with a lead of two in the block, and for a player of one state of an @state game.

The rules:

  • The names are those before the colon in @optimization_problem (player2:@objective = ...). In an @optimization_problem with @state blocks, a name exempts the player of that name in every state where it plays, and no player of the other states. A name that is no player in any state, and a name given twice, are refused.

  • Several players may be named, @outside_optimization(home, foreign){B_F}. A block naming every player of every state is the every-player form @outside_optimization{B_F}: its equations leave the constraints altogether and the multipliers are numbered with gaps where they sit.

  • As in the every-player form, the braces list as many variables as the block has equations. A player may not take a variable as given in two blocks, nor take its own instrument as given.

  • An exempt leader of a leader-follower game never takes derivatives with respect to the exempted variables either, also where they enter the followers’ first-order conditions, which it keeps as constraints.

  • The auxiliary equations that RISE adds for the leads and lags beyond one in the equations of a block belong to the block, in both forms: for B{t} = 0.5*B{t+2} + X{t}, the auxiliary LEAD_1_B{t} = B{t+1} is exempted with the block, and its variable LEAD_1_B is taken as given (never differentiated). An auxiliary that an equation outside the block shares (the same lead of the same variable in @model) stays a constraint of every player. The tests find the solution written by hand, with no multiplier on the auxiliary and no feedback warning.

Limits.

  • The linear-quadratic solvers (loose_commitment, stochastic_replanning) take every model equation as a constraint of every player and refuse models with @outside_optimization, in either form.

  • Square brackets after the keyword are kept for options; none exist yet.

The tests are in RISE-unit-tests, models/dsge/optimal_policy/outside_optimization.

1.8.7. Robust policy: a policymaker that distrusts its model

A policymaker may not fully believe the model it is optimising against. It then considers a set of models close to the reference one and asks what happens if the least favourable of them is true. Declare that with @robustness:

@parameters(pol,2) rho_m

@optimization_problem{
    @objective   = -0.5*(pi^2 + lambda_y*y^2 + lambda_i*i^2),
    @robustness  = rho_m,
    @instrument  = i,
    @discount    = betta
}

That is the whole of it. The objective stays the ordinary loss: you do not write the adversary, its instruments, or any entropy penalty.

rho is the robustness intensity, and it belongs to the policymaker in exactly the way risk aversion does. It prices how far the policymaker entertains its model being wrong: rho = 0 is complete trust and the ordinary problem, and larger rho buys more insurance against misspecification. It must name a parameter, never a number, so that it can be calibrated, estimated, and – as above – attached to a Markov chain, which lets a policymaker fear misspecification more in one regime than in another.

Note

rho = 0 is expressed by omitting @robustness. A model that does not declare it is untouched: no distortion variables are created and the answer is the ordinary one, not an approximation to it.

What RISE builds for you

A robust policymaker behaves as if an adversary were choosing the most damaging misspecification the model still admits. That is a two-player zero-sum game, which RISE already solves, so robustness needs no new solver – only the game written out, and RISE writes it:

  • one distortion instrument per shock, named DISTORT_<SHOCK>, so a shock EPS gains DISTORT_EPS. A distortion is a lie about where a shock is centred, so it enters inside that shock’s own coefficient, and the same distortion appears at every occurrence of the shock – one innovation, one lie;

  • a one-period lag, so the adversary commits before seeing the innovation it distorts. Your own instrument keeps its dating;

  • the adversary’s objective: yours, negated, with the entropy term carried by both. It is one objective faced with two signs, which is what makes the equilibrium a min-max rather than an ordinary Nash point;

  • the adversary one @order behind you, since min_u max_w is the ordering the robust problem is written in.

The distortions are ordinary endogenous variables: they appear in the solution, in impulse responses and in simulations. That is deliberate. DISTORT_EMU’s response to the state answers “how much worse is the cost-push process assumed to be, and when” – which misspecification the policy is guarding against is usually the point of the exercise.

Both routes solve it, and every timing protocol composes with it.

The frontier

Robustness is not free of bounds. Beyond a model-specific frontier the adversary can buy unbounded damage and the problem has no value:

\[\rho < \bar\rho_j = \left[\lambda_{\max}\!\left(C_j' \bar P_j C_j\right)\right]^{-1}\]

The frontier is an eigenvalue condition, so it moves with the model, and under regime switching it couples regimes: how robust you may be in a calm regime depends on the value in every regime it can reach. It also moves as you approach it, since the value on the right is the value under the robust policy. Crossing it is reported as a solver failure.

Warning

The frontier can be small. On an estimated New Keynesian model it sits near 3e-4, and the policy rule deforms sharply as it is approached. Bracket it before choosing rho rather than guessing.

Restrictions

  • One robust policymaker. Declaring @robustness for more than one player of a game is refused. Each would fear a different misspecification, so the problem stops being zero-sum and becomes a fixed point in rules – a different object, not a bigger version of this one.

  • Anticipated shocks are not distorted. A lie about a shock agents have already been told about is a coherent but different object.

1.8.8. Solve-time choices

Two orthogonal options on solve:

Option

Values

Default

solve_policy_type

'ramsey', 'discretion' (case-insensitive)

'ramsey'

solve_policy_equilibrium

'OLE', 'MPE' (case-insensitive)

'OLE'

solve_policy_type:

  • 'ramsey' – full commitment; the planner re-optimises at t = 0 and not again.

  • 'discretion' – time-consistent / Markov-perfect policy; the planner re-optimises every period.

solve_policy_equilibrium matters only for multi-player games:

  • 'OLE' – open-loop equilibrium. Each player optimises against the opponent’s path. The forward-shadow row of each FOC is the classical forward-multiplier term.

  • 'MPE' – Markov-perfect equilibrium. Each player optimises against the opponent’s policy function. The forward-shadow row picks up an additional cross-player chain-rule term that captures how the opponent’s instrument responds to the predetermined state.

All four combinations are supported:

m = solve(m, solve_policy_type = "ramsey",     solve_policy_equilibrium = "OLE");
m = solve(m, solve_policy_type = "ramsey",     solve_policy_equilibrium = "MPE");
m = solve(m, solve_policy_type = "discretion", solve_policy_equilibrium = "OLE");
m = solve(m, solve_policy_type = "discretion", solve_policy_equilibrium = "MPE");

Restrictions on solve_policy_equilibrium

  • Stochastic-replanning models reject the option. Loose-commitment and stochastic-replanning models are managed through the commitment switching parameter and a Markov chain. Calling solve_policy_equilibrium = ... on such a model errors. These model classes retain their OLE-equivalent behavior.

  • ``@no_u_turn = true`` silently degenerates MPE to OLE. The flag asks RISE to skip the discretion-derivative pipeline, which the MPE chain-rule contribution is built from; under @no_u_turn = true the cross-chain is zero by construction. If you want MPE, leave @no_u_turn at its default (false).

  • Single-player consistency. With one player, MPE = OLE exactly. Toggling the option has no observable effect.

  • Stackelberg. The follower’s set of differentiation variables already includes the leader’s instruments and multipliers, so the cross-chain is correctly built over the same-level Nash opponents only. No user action is required.

1.8.9. Solver selection

RISE picks an appropriate solver automatically based on the model shape:

  • Constant-parameter problems that eigenvalue methods can handle use the default RISE solver rise_1. Discretion can also be solved with rise_1, conditional on the solution of the forward-looking variables.

  • Regime-switching problems use the functional-iteration solver mfi.

  • When @no_u_turn = true a linear-quadratic solver is used – loose_commitment or stochastic_replanning. Both work well in linear-quadratic setups but are less accurate for nonlinear problems, and neither supports higher-order perturbation.

  • Neither linear-quadratic solver takes an optimization problem with @state blocks, whose policymakers change with the regime (see What the solvers refuse, and why below). Such a model is solved by the general solver; below full commitment it needs the discretionary machinery, so parse it with @no_u_turn = false (the default).

You can override the choice with solver = '+name' – per the diagnostic protocol in Solving, this is the right move when the default solver returns retcode 21 on a model you believe is determinate.

1.8.10. Perturbation types

solve_perturbation_type applies to an optimal-policy model as to any regime-switching model (see Perturbation types for regime-switching models). When a switching parameter moves the steady state – an inflation target, a loss weight, a parameter of the constraints – perturbation M with the self-consistent linearization expands around the rest points of the shock-free switching economy instead of each regime’s isolated steady state, and 'bm' around the P-weighted anchors of Barthélemy and Marx:

m = solve(m, solve_policy_type = "ramsey",     solve_perturbation_type = {'m','scl'});
m = solve(m, solve_policy_type = "discretion", solve_perturbation_type = {'m','scl'});

Nothing in either is specific to optimal policy: the first-order conditions are re-anchored like any other equations, the multipliers’ rest points move with the other variables’, and under discretion the policy functions of next period’s regime are evaluated where the leads are, at the transition points (regime \(j\)’s policy re-expanded at regime \(i\)’s rest point). With a common steady state across regimes both return plain 'm'. The report is the usual one, model_data.state_space{1}.scl (or .bm), and get(m,'sstate') returns the rest points. The first order is solved by the solver the model is configured with, the linear-quadratic ones included, and each solver applies its own requirements to the derivatives it is given (see When the structural matrices also switch below for those of the linear-quadratic solvers).

1.8.11. The planner-discount knife-edge

When the planner discounts like the agents, the multiplier of a constraint with a lead obeys \(\mu_t = (\beta/\delta)\mu_{t-1}\) (\(\delta\) the planner’s discount, \(\beta\) the agents’), whose roots meet at one when \(\delta = \beta\). The multiplier steady state can then be a continuum (a variable used only for reporting, such as \(W_t = X_t + \beta W_{t+1}\), is enough), and paired multiplier roots can coincide at one. RISE returns the limit as the planner’s discount goes to the agents’: the steady state is selected exactly, regime by regime, and the decision rules are extrapolated from solutions at nearby planner discounts (a hidden parameter planner_discount_scale, of value 1) when the solution has such unit roots. When the continuum has no limit, the solver’s point is kept and the warning rise:optimal_policy:multiplier_continuum says so. A variable used only for reporting is better put outside the optimization (Equations outside the optimization above), where its equation has no multiplier at all.

The extrapolation runs only when the solution has unit roots that the planner-discount scale moves off the unit circle. For a constant-parameter model the roots are the eigenvalues the solver reports. The solvers of regime-switching models (functional iteration, Newton) report none; for a switching model the roots are then those of each regime’s solved transition of the state variables. Under the self-consistent linearization ({'m','scl'}) and the Barthelemy-Marx anchors ('bm') the expansion points of a switching model are a continuum at the knife-edge too, and are extrapolated with the decision rules; under 'm' the steady states are kept. A constant-parameter model solved by a method that reports no eigenvalues is left as the solver returns it.

Under 'bm' the P-weighted anchor condition itself has a continuum at the knife-edge; the solution is then the limit of those at the nearby planner discounts. One limit remains: a model with genuine unit roots (a price level) and an optimal-policy block pays one extra solve per solve call, the check at the first nearby planner discount.

1.8.12. Loose commitment and stochastic replanning

These are the cases where a switching commitment parameter does live in the model file – the regime is whether the planner is currently honouring commitment or has re-optimized.

Loose commitment – constant re-optimization probability, linear-quadratic:

@optimization_problem[@no_u_turn = true]{
    @objective = pi^2 + lambda_y*y^2,
    @discount  = beta
}

@parameters(looseCommit, 2) commitment
@parameters looseCommit_tp_1_2 looseCommit_tp_2_1

The chain must have exactly two states; you choose which state has commitment = 1 (commitment-active) vs commitment = 0 (discretion-active).

Stochastic replanning – possibly time-varying probability, nonlinear:

@optimization_problem[@no_u_turn = false]{
    @objective = -0.5*(pi^2 + lambda_y*y^2),
    @discount  = beta
}

@parameters(stochrepl, 2) commitment

The transition probabilities may be exogenous parameters (stochrepl_tp_1_2, stochrepl_tp_2_1) or endogenous via the @transition_functions block – see Time-varying transition probabilities.

To pin looseCommit_tp_1_2 = 1 - looseCommit_tp_2_1 for estimation, use the same linear restriction syntax as for any other parameter pair; see legacy Estimation restrictions.

1.8.13. Loose commitment and stochastic replanning are different problems

The two routes are not two solvers for one problem. They formalise re-optimization differently. Two distinct objects are involved:

  • the probability of re-optimization — the chance the standing plan is torn up next period. Under loose commitment this is \(\gamma\); under stochastic replanning it is carried by the transition matrix of the commitment chain;

  • the regime indicator commitment — which state the economy is in right now. Loose commitment has no such object; stochastic replanning does.

Important

The indicator does not replace the probability. Stochastic replanning uses both, and the re-optimization probability drives its policy functions exactly as \(\gamma\) drives the loose- commitment rule. It enters through the transition matrix, which weights the expectation over next period’s regime and so appears in every forward term.

The point is easy to check. Hold the regime structure fixed — two commitment regimes, two discretion regimes, chain i.i.d. so the loose-commitment restriction holds — and vary only \(\gamma\). Distance of the SR rule from the two pure solutions, on a scale where pure commitment and pure discretion are \(6.98\) apart:

\(\gamma\)

commit. regime, vs pure commit.

commit. regime, vs pure discr.

discr. regime, vs pure discr.

discr. regime, vs pure commit.

0.10

4.28

2.71

0.90

6.08

0.50

1.90

5.08

3.87

3.11

0.90

0.30

6.68

5.90

1.08

0.99

0.03

6.95

6.25

0.73

A planner sitting in the discretion regime is nowhere near Markov-perfect discretion unless \(\gamma\) is small: it has re-optimized today but still expects to be bound tomorrow with probability \(\gamma\), and it prices that. If the indicator negated the probability, the bold column would be near zero and flat in \(\gamma\). It is neither.

The genuine difference is narrower, and local to one coefficient — see What weights the policy-derivative channel below.

How many decision rules there are. Under loose commitment the planner who re-optimizes at date \(t\) faces the same objective, the same constraints from \(t\) forward, no inherited promise, and the same prospect that its own successor re-optimizes with probability \(1-\gamma\). That is the date-0 problem again, so it must select the same policy function. The re-optimization event is therefore not a regime: it is a value of the state, namely \(\lambda_{t-1}=0\). One rule per structural regime suffices, and the LQ engine’s solution does not depend on the commitment chain at all — GAM1 and the raw H are identical across the two commitment states.

Under stochastic replanning the replanning state is payoff-relevant in its own right, may be persistent, and carries its own decision rule. There are as many rules as composite regimes.

Where the reset is applied. Loose commitment resets the state: the reported solution for a discretion regime is \(H_i\mathsf R\) with \(\mathsf R=\mathrm{diag}(I,0)\), computed by loose_commitmentize zeroing the multiplier columns after the fixed point. Stochastic replanning resets inside the equations: commitment multiplies the promise term in the FOC, so the term is absent in a discretion regime.

Warning

Because loose_commitmentize runs after the fixed point, the reported Tz for a discretion regime is not the raw fixed point. Anything that re-derives residuals, re-solves from the reported solution, or interprets the multiplier block must account for it.

The difference, column by column. That has a sharp consequence, and it is the cleanest single statement of what separates the two. Split the state columns into Lagrange multipliers and the rest, then compare the commitment-regime rule against the discretion-regime rule within one structural regime:

\(\gamma\)

route

non-multiplier cols differ by

multiplier col differs by

discretion’s multiplier response

0.9

LQ engine

0.000e+00

0.519

0

0.9

general

0.779

0.515

0

0.5

LQ engine

0.000e+00

0.599

0

0.5

general

1.216

0.557

0

Not approximately zero – identically. The LQ engine’s two branches respond to every non-multiplier state in precisely the same way; the sole difference is the multiplier column, which the discretion branch discards. Both routes zero that response, and their commitment-branch multiplier responses nearly agree. So the entire \(0<\gamma<1\) disagreement between the two routes lives in the non-multiplier columns of the discretion branch – the one place the LQ engine has no freedom.

A summary that survives the measurements:

Stochastic replanning is genuine switching between two jointly-determined rules – a commitment branch and a replanning branch, each anticipating the other – under an arbitrary, possibly transition process. Loose commitment collapses that to a single rule plus a state reset: its two branches are tied to coincide on every non-multiplier state column, differing only in that the replanning branch discards the inherited promise.

Two cautions on reading that. “Tied” describes the algorithm, not an assumption imposed on two otherwise-free rules – the re-optimizing planner faces an identical problem and must therefore choose the same policy function, so the tie is an implication. And the general route’s two branches are not the pure commitment and pure discretion solutions: at \(\gamma=0.9\) its commitment branch sits 0.30 from pure commitment and its replanning branch 5.90 from pure discretion – and only 1.08 from pure commitment – on a scale where the two pure solutions are 6.98 apart. A planner who re-optimized today still expects to be bound tomorrow with probability \(\gamma\), and prices that.

This also makes the endpoint result immediate: at \(\gamma\in\{0,1\}\) one branch ceases to exist, so “two rules tied off the multiplier columns” and “two rules free to differ” describe the same object.

The same term, with a different scalar in front of it. Both formulations must price the branch in which tomorrow’s planner is not bound by today’s promise. That is the branch in which the derivative of the future policy function with respect to the current state enters — \(H^{yy}\). Both routes carry that same object. The LQ engine writes it directly as sum_j Aplus{i,j}*H_yy(j). The general route writes it as the f1942 hyperparameters, which are not a different construction: their derivative entries are filled from the solution’s own Tz, in +optimal_policy/+discretion/update_coefficients.m:

Tzi = Tz{ireg}(discr_check_against_vars, cols);
coefs(ff,:,ireg) = this.';

So in the linear-quadratic case f1942' is \(H^{yy}\). It is the general nonlinear machinery for the same derivative – which is exactly why the LQ route is attractive as a way of reaching discretion without it.

What differs is the scalar multiplying that shared term:

loose commitment  :  (1-gamma)          * [ sum_j Aplus{i,j}*H_yy(j) ]
stochastic replan :  (1-commitment_t)   * [ the same derivative      ]

A probability against an indicator, in front of one common object. They coincide only when the indicator is constant across regimes and equal to the probability – at \(\gamma=1\) (both zero) and \(\gamma=0\) (both one), and nowhere in between. That single scalar is the whole difference.

Everything else – the derivative itself, and the role of the re-optimization probability in propagating it – is common to the two, which is why both rules move with \(\gamma\) in the table above.

Other constraints. The LQ engine requires the replanning process to be i.i.d.: Pr(commit next) = gamma from every state, checked against the transition matrix and an error otherwise. It is linear-quadratic and first order by construction. The general route imposes none of this, and neither does the linear-quadratic replanning solver described below.

Summary

loose commitment (LQ engine)

stochastic replanning (general route)

re-optimization probability

\(\gamma\), a scalar

carried by the chain’s transition matrix

regime indicator

none — re-optimization is a state value, not a regime

commitment, per composite regime

decision rules

one per structural regime

one per composite regime

replanning process

must be i.i.d.

any, incl. persistent / endogenous

reset of promises

on the state, after the solve

in the equations

policy-derivative weight

\(1-\gamma\)

\(1-\texttt{commitment}_t\)

approximation

LQ, first order only

any order, no LQ assumption

What is measured

On a small LQ policy problem solved both ways (see models/dsge/optimal_policy/switching_loose_commitment), with a Markov chain switching the structural coefficients:

  • \(\gamma=1\) and \(\gamma=0\): the two routes agree, and each solution satisfies the other’s equilibrium conditions — residuals of \(4\times10^{-9}\) to \(6\times10^{-9}\), against \(10^{-14}\) for a solution in its own system. The agreement is robust to solve_initialization (zeros, backward, random), so it is not an artifact of a shared starting point.

  • \(0<\gamma<1\): they disagree, and neither solution solves the other’s system, so it is not a matter of picking different roots of one problem. The gap does not shrink as \(\gamma\to1\): it is about 13% of the decision rule at \(\gamma=0.99\) and collapses to \(10^{-16}\) only at \(\gamma=1\), when the discretion regime ceases to exist.

  • The gap reaches the allocations whenever an endogenous state feeds back. In a model whose only state is the exogenous forcing process it is confined to one multiplier and the allocations agree to \(4\times10^{-13}\), which is misleading; adding indexation so that inflation is itself a state moves the allocations by 10–14%.

Which to use

Use the LQ engine when the object you want really is Debortoli–Maih– Nunes loose commitment: one plan, i.i.d. abandonment, promises reset on re-optimization. Use the general route when the replanning state is meant to be persistent, forecastable or endogenous, when the model is not LQ, or when you need higher-order accuracy.

Do not treat a disagreement between the two at \(0<\gamma<1\) as a bug in either. The regression suite records the disagreement deliberately (test_LC_vs_SR_Discrepancies).

Note

Other imperfect-commitment algorithms in the literature — Schaumburg–Tambalotti quasi-commitment, and the Blake–Kirsanova family — make their own choices on exactly these points, so their output need not match either route here. Two of those models ship with the regression suite (CampbellKirsanovaLeith/ckl.rs, Blake and Kirsanova 2010/bk2010.rs). Blake and Kirsanova also show that discretionary LQ policy can admit multiple Markov-perfect equilibria, so on some parameterizations the solution an iterative solver returns is initialization-dependent; RISE exposes solve_initialization for exactly that reason. No multiplicity was found on the fixture above, but that is one model, not a general result.

1.8.14. A linear-quadratic solver for stochastic replanning

solver = 'stochastic_replanning' solves the stochastic-replanning problem with the same linear-quadratic machinery the loose-commitment solver uses, rather than through the parser and the general MSRE solver:

ms = solve(m, 'solver', {'stochastic_replanning', 'MaxIter', 20000});

It is the loose-commitment engine with the scalar \(\gamma\) replaced by a per-regime commitment indicator \(c_r\), read off the same commitment parameter. Three things follow.

The transition matrix is unrestricted. loose_commitment requires \(\Pr(\text{commit next})\) to be the same from every regime and errors otherwise – it has to, because \(\gamma\) is one number. Writing \(p_{12}=\Pr(\text{commit}\to\text{replan})\) and \(p_{21}=\Pr(\text{replan}\to\text{commit})\), that restriction is

\[1-p_{12} = p_{21} \qquad\Longleftrightarrow\qquad p_{12}+p_{21}=1,\]

a one-dimensional slice of the unit square. Every other transition matrix has no loose-commitment counterpart whatever – and those are precisely the cases this solver exists to reach. Reading the chain directly there is nothing to check, so a replanning state that is more persistent than the commitment state, or less, or absorbing, is admissible on the same footing.

Regression-tested on a 5x5 grid over \((p_{12},p_{21})\) independently: 20 of the 25 cells are off the slice, loose_commitment refuses exactly those 20 and accepts exactly the 5 on it, and this solver reproduces the general route in all 25 (worst disagreement \(8.8\times10^{-8}\)). The absorbing corners \(p_{12}=0\), \(p_{21}=0\) and both-zero work too, though they leave regimes unreachable, which the de-weighting of the promise block has to survive and does.

The commitment chain need not be the only chain. Everything is indexed by the composite regime, so any number of other Markov processes may switch the structural matrices, the loss weights or the shock loadings alongside it, with any number of states each. The commitment indicator is read off the composite grid, wherever the commitment chain happens to sit in the ordering. Regression-tested on three chains – a 3-state chain carrying the forward coefficient and the slope, a 2-state chain carrying the shock loading, and the 2-state commitment chain – giving 12 composite regimes, with the indicator interleaving as [1 1 0 0 1 1 0 0 1 1 0 0].

The commitment chain itself must have exactly two states, but that is a parser restriction (consolidate_parameters) applying to every optimal-policy route, not something this solver imposes.

The reset is in the equations. GAM1(y,lamb) is gated by \(c_r\), so only a commitment regime inherits a promise and the zero multiplier response of a replanning regime falls out of the fixed point. There is no loose_commitmentize step, so the reported Tz is the fixed point – unlike under loose_commitment, where it is not.

It survives no_u_turn. Like the loose-commitment solver it computes the derivative of the future policy function inside its own fixed point, so it never needs the discretionary (f1942) machinery that @no_u_turn = true drops at parse time. On a model parsed that way with a persistent replanning chain it is the only route that solves at all: loose_commitment refuses the chain, and the general route refuses the missing derivatives.

Choosing between the three

loose_commitment

stochastic_replanning

general (mfi, …)

problem solved

loose commitment

stochastic replanning

stochastic replanning

replanning chain

i.i.d. only

any

any

reset applied

after the solve

in the equations

in the equations

needs f1942

no

no

yes

approximation

LQ, first order

LQ, first order

any order, no LQ

Use loose_commitment when you want Debortoli–Maih–Nunes loose commitment. Use stochastic_replanning when you want the replanning state to be a genuine regime and the model is linear-quadratic. Use the general route when the model is not linear-quadratic or when you need higher-order accuracy.

Note

Weighting the policy-derivative channel by the probability that the successor replans, rather than by the current regime indicator, turns this solver back into loose commitment – exactly, on the i.i.d. edge. That variant is not exposed, because off that edge it has nothing to be validated against.

Validation

Against the general route, over the full reported solution (states, sigma column and shock impacts): agreement to \(10^{-8}\) or better at \(\gamma=1\), at \(\gamma=0\), on i.i.d. chains with \(0<\gamma<1\) – where loose_commitment disagrees with the general route by \(O(1)\), since it is solving the other problem – and on persistent chains that loose_commitment refuses. See models/dsge/optimal_policy/switching_loose_commitment in the test suite.

1.8.15. When the structural matrices also switch

Loose commitment leaves the re-optimization probability \(\gamma\) constant, but nothing stops the rest of the model from switching on another chain. That interacts with the promise term, and the interaction is easy to get wrong.

\(\lambda_{t-1}\) enters the date-\(t\) optimality condition because \(y_t\) sat in the lead term of the date-\((t-1)\) constraint. The promise term therefore carries the forward matrix that constraint attached to \(y_t\), scaled by \(\beta^{-1}\):

\[\beta^{-1} \left(\frac{A^{+}_{\iota,r_t}}{q_{\iota,r_t}}\right)' \lambda_{t-1}, \qquad \iota = r_{t-1}\]

The transition probability is divided out because the measure already supplies it, and the pair \((\iota,r_t)\) is the realised one – an average over the second index is not the same object.

Which regime that matrix belongs to is decided by how you date the coefficient in the model file, and the two choices are not equivalent:

a*PI{t+1} (the default reading)

RISE evaluates a at the current regime, so \(A^{+}_{ij} = q_{ij} A^{+}_i\) and the de-weighted block is \(A^{+}_{r_{t-1}}\): the promise matrix belongs to the regime in which the promise was made. No solution indexed by the current regime alone can carry that: the parser carries the promise as a state, which both linear-quadratic solvers refuse (see below).

a{t+1}*PI{t+1}

The coefficient is dated at \(t+1\), so \(A^{+}_{ij} = q_{ij} A^{+}_j\) and the de-weighted block is \(A^{+}_{r_t}\): a function of the current regime only. The problem closes on \(h\) regimes with no augmentation and the loose_commitment solver computes it exactly.

Both readings agree, and no warning is issued, whenever the forward coefficients do not switch – which is why constant-matrix loose commitment was never affected. A coefficient that switches on a contemporaneous or lagged term is likewise unaffected: only the lead term feeds the promise.

What both linear-quadratic solvers require, then, is that the de-weighted lead block \(A^{+}_{\iota,r_t}/q_{\iota,r_t}\) of the structural matrices they are given be a function of \(r_t\) alone. Whatever makes it depend on \(\iota\) as well – a current-dated switching coefficient on a lead (which the parser carries as a promise state, see below), or lead derivatives taken at a point that depends on both regimes of the pair – the solver stops with the error RISE:looseCommitment:leadBlockDependsOnPreviousRegime (or RISE:stochasticReplanning:leadBlockDependsOnPreviousRegime), which names the regime and the size of the dependence. Both solvers solve the first order only; a higher solve_order stops with RISE:looseCommitment:firstOrderOnly (or RISE:stochasticReplanning:firstOrderOnly) before anything is solved.

Carrying the promise as a state

Under the current-dated reading the parser carries the promise as a state rather than as a dated coefficient. For each differentiated variable it mints one promise state

\[\mu_j(t)=\sum_{\text{rows}}\lambda_t(\text{row})\cdot \frac{\partial c(t)}{\partial x_j(t{+}1)}\]

– everything at \(t\), so nothing needs dating at all – and the first-order condition carries \(\beta^{-1}\mu_j(t{-}1)\). That is linear in a state, and \(\mu_j(t)\) linearises to \(\omega_{r_t}\hat\lambda_t\), so \(\mu_j(t{-}1)\) is \(\omega_{r_{t-1}}\hat\lambda_{t-1}\): the predecessor regime carried as a genuine first-order state. You write a*PI{+1} and the parser renders it correctly; nothing is required of you.

Dating the coefficient instead does not work, which is why this is done. The emitted term is then a product of two lagged states whose steady states are \((\omega_{ss},0)\), and the multiplier’s zero steady state kills the coefficient channel at first order. Measured against a pair-indexed reference solution on a two-regime model with \(a=(0.99,0.50)\), the old form sat 0.441 from the truth; the present one matches an independently validated oracle to \(2\times10^{-16}\).

The trigger is a bare switching symbol in the block. A lead-dated coefficient arrives already tagged and is skipped, so the a{t+1} convention mints nothing, and a model with no switching coefficient on a lead inside a constraint emits exactly what it emitted before.

Warning

The two linear-quadratic solvers refuse a model carrying a promise state. Their constraint block drops multiplier columns by construction, so the promise state’s defining equation has nowhere to live. Use the general route, which renders these models correctly, or date the coefficient at \(t+1\) – a different model.

1.8.16. Solving a non-cooperative game without the policy-derivative machinery

The general route solves a game by approximating every player’s decision rule with a polynomial and iterating on that approximation, re-evaluating the model’s derivatives and re-solving the steady state at each pass. That is the f1942 machinery, and it is what makes games expensive.

In a linear-quadratic problem the rules are linear and the coefficients the loop is searching for are entries of the solution matrix. Writing them down instead of searching for them is the specialisation, and it is now available on the two linear-quadratic solvers:

% Markov-perfect Nash under discretion
[md,rc] = solve(m,'solve_policy_type','discretion', ...
                  'solve_policy_equilibrium','MPE', ...
                  'solver','loose_commitment');

% per-regime commitment indicator
[md,rc] = solve(m,'solver','stochastic_replanning');

No new solver name was introduced. The re-optimisation protocol is the solver; the number of players, and how they are ranked by @order, are properties of the model. They are orthogonal, and both solvers accept a Nash game, a Stackelberg hierarchy, or any mixture of levels.

What a game adds

Exactly one term. Player p prices the constraints with its own shadow prices, and the date t+1 constraint reaches date t not only through the lag block but through every rival’s instrument, which moves with the state:

\[\widetilde{A}^{-}(p,r_j) = A^{-}_{r_j} + (1-\mathrm{ole})\sum_{q \in \mathrm{rivals}(p)} A^{0}_{r_j}[\mathcal{C},u_q]\, H_{r_j}[u_q,\cdot]\]

Substituting this augmented lag block for the ordinary one, and giving each player its own loss curvature and multipliers, is the whole of the generalisation. Commitment, discretion and stochastic replanning need no separate derivation.

Everything in the expression above is read at the realised future regime \(r_j\), and that is forced. The term comes from \(\beta\,E_t[\lambda^p_{t+1}{}'\,dc(t+1)/dy_t]\), and \(c(t+1)\) is the constraint at regime \(r_{t+1}\); its lag block and its derivative with respect to the rival’s instrument are coefficients of the same date-\((t+1)\) equation, so both belong at \(r_{t+1}\).

Note

Both routes used to read the second one at \(r_t\), and it cost 4.3e-05 of equilibrium error whenever a rival’s instrument carried a switching coefficient.

In the parser the cause was mechanical. mpe_envelope_inject builds the cross term at \(t\) and shifts it forward with local_lead, which moves time-tagged atoms only – the multiplier and the variables. A bare parameter carries no tag, so the shift walked past it and it stayed at the equation’s own regime, leaving a single coefficient with its lag half at \(r_{t+1}\) and its rival half at \(r_t\).

Both are fixed. Measured against the exact Markov-perfect Nash condition (best-response residual): 5.5e-12 for the linear-quadratic route, 1.9e-12 through the parser, and the two agree to 2.1e-10. When the rival’s coefficient does not switch, the two readings coincide and nothing changes.

The fix tags switching parameters only, so it mints one auxiliary variable per switching parameter that actually sits on a rival’s instrument and nothing otherwise. A 34-variable two-country game whose switching parameters are Phillips-curve slopes grows by zero variables; solve times are unchanged.

Note also that rise_1 is not designed for regime switching – especially with switching in the dynamics or non-extreme transition probabilities – and scores 2.6e-03 on this test for that reason, independently of anything above. Use mfi or another switching-capable solver.

A player internalises the reaction of every player whose @order does not exceed its own, itself excluded, which is the same rule the parser applies. For same-level Nash that is “all the others”.

The choke point, and what now guards it

Every coefficient that has to travel from \(t\) to \(t+1\) in the optimal-policy pipeline goes through one regexprep in the parser, local_lead, and that expression moves time-tagged atoms only. Variables and multipliers carry a tag; a bare parameter does not, so it does not move – it stays bound to the equation’s own regime while every coefficient beside it lands at \(r_{t+1}\).

For a constant parameter that is harmless: its value is the same at every date. For a switching parameter it is a mis-dating, and an invisible one. Nothing errors, nothing warns, and the answer is wrong by an amount nobody measures unless they go looking. It is also invisible with a single regime, which is why it survived as long as it did.

local_lead now refuses to run on a block that still carries an undated switching parameter, naming the parameter. Callers date them first; a caller that forgets gets an error instead of a number. Exactly two symbols are exempt, because their timing genuinely is not the constraint’s: commitment, which asks whether an inherited promise is honoured now, and ole, which selects the equilibrium concept and is the same at every date.

Leader-follower games

Players may carry distinct @order values, in which case the game is a Stackelberg hierarchy rather than Nash. The parser eliminates players by @order descending, so the highest goes first and its first-order conditions become constraints for whoever is processed after it:

Important

Lower @order = leader.

A leader therefore prices the ordinary equations plus the conditions of every player eliminated before it. Those conditions are rows of the very system the linear-quadratic engine writes, so a leader’s dated blocks are nothing more than a row-restriction of the matrices being built. Assemble the players in processing order and the rows a leader needs are already there: no new algebra, and it composes with the timing exactly as Nash does.

The second derivatives that made this look intractable – a leader differentiates its followers’ conditions, which carry derivatives of every tracked decision rule – vanish under LQ, because those rules are linear and their coefficients are constants.

Two channels are at work and neither substitutes for the other:

  • a leader internalises its followers within the period, through the multipliers it carries on their conditions;

  • a follower internalises the leader’s future reaction, because its own actions today move the state the leader will act on tomorrow – the \(\widetilde{A}^{-}\) term above, whose rival set is every player whose @order does not exceed the player’s own.

Verified block by block against the parser at its converged coefficients on a two-player hierarchy: for the follower A0/A+/A- differ by 0/6.3e-11/0 and for the leader by 9.1e-12/0/0. Against the general route the solutions agree to 1.6e-09 backward-looking, 1.9e-10 with a switching coefficient on a rival’s instrument, and 1.4e-07 forward-looking, which is the reference’s own accuracy.

Note

Bookkeeping trap. player_order, player_constraint_rows, player_multipliers and mult_groups are indexed by processing order – @order descending – whereas the parser’s declaration list is not. With one player per level the two coincide, so a swapped index would go unnoticed; with two players sharing a level above a follower it would not. That is what game_rf3lf in the regression suite exists to catch.

Markov perfection is a discretionary concept

MPE and OLE coincide under commitment. The general route says so by construction: it enters its coefficient loop on any(commitment ~= 1) and never on the equilibrium concept, so with a commitment regime everywhere the tracked policy derivatives stay at zero and the cross-player chain term is simply absent.

The linear-quadratic engine computes those derivatives itself, so nothing stops it applying the chain term anyway – and it did, which put it 1.6e-02 of the decision rule away from the general route on a two-player backward-looking game under solve_policy_type='ramsey' with MPE. It now reads the same quantity the general route reads, and the two agree exactly.

Options: there are no new ones

The solution is computed by two nested loops. Two coefficients depend on the decision rules – the augmented lag block above, and the total derivative of the constraint that a re-optimising player prices. The outer loop updates those while the inner one solves an ordinary constant-coefficient problem. That is the same structure, over the same objects, as the general route’s own coefficient loop, so it is governed by the same two settings:

setting

governs

solve_discretion_maxiter

iterations of the coefficient loop, floored at 50 – identical to run_loop_local in +solve/solve.m

solver{2}.TolFun

its tolerance – again identical to the general route

Tighten either one and both routes respond the same way. Nothing was added to RISE’s option list for them.

The one genuinely engine-specific choice is which solver handles the inner problem, because the solver name that selected this engine (loose_commitment / stochastic_replanning) would not dispatch there. That travels in the solver’s own options rather than becoming a property of RISE:

solve(m,'solver',{'stochastic_replanning','MaxIter',20000, ...
                  'InnerSolver','mnk'});

It defaults to 'mfi'. Any switching rational-expectations solver will do; it never sees a policy-derivative function.

What the solvers refuse, and why

Two cases are refused with a reason rather than answered:

Loose commitment with a commitment probability strictly inside (0,1). The probability would have to scale each player’s forward multiplier block, and that scaling cannot be expressed in the form the shared first-order engine reads. Use stochastic_replanning, which represents re-optimisation by a per-regime indicator and needs no such scaling.

An optimization problem with @state blocks. With @optimization_problem[@markov_process=pol]{@state(1){...} @state(2){...}} the policymakers change with the regime: each state has its own players and its own multipliers, which are zero in the other states. Both linear-quadratic solvers solve one optimization problem and have no player set per regime, so they refuse such a model when they are chosen, with the error RISE:optimalPolicy:lqSolversStates, whatever the policy type and however many players each state has. Solve it with the general solver. Below full commitment the general solver needs the discretionary machinery: a model parsed with @no_u_turn = true is refused under discretion with RISE:optimalPolicy:noUTurnStates; parse it with @no_u_turn = false (the default).

Those are the only cases refused.

The promise coefficient is an expectation

The block a leader’s inherited promise carries is not the realised one. It is its expectation over the successor regime, conditional on landing in a regime that honours the promise:

\[\bar{A}(r_t) = \frac{\sum_j Q(r_t,j)\,\mathbb{1}\{c_j>0\}\,A(j)} {\sum_j Q(r_t,j)\,\mathbb{1}\{c_j>0\}}\]

The two readings coincide unless the promise block varies across the regimes that honour it, and that needs three things at once: a hierarchy, so a leader holds promises on its followers’ conditions; a commitment indicator that differs across regimes, so a promise can span a change of status; and a switching structural coefficient, so its value differs by regime. Any two of the three agree. The triple did not, by 1.3e-03, until the linear-quadratic engine was taught to read it the same way; the two routes now agree to 7.3e-09.

Because the transformation is the identity when the block does not vary across honouring regimes, no model that already agreed was affected – which is also why the case had to be built before the difference could be seen at all.

Three or more @order levels

Supported and tested. The construction is a forward pass over processing order and is generic in the number of levels – at the third, the row map resolves rows belonging to two different earlier groups rather than one. game_rf3x (orders 1/2/3, one player each) prices 3 / 7 / 15 rows and agrees with the general route at 2.1e-10 (MPE) and 4.3e-10 (OLE).

The re-optimiser faces the reset rule

Two channels in this engine stand in for what the general route evaluates with f1942: the re-optimiser’s continuation \(\sum_j C^{+}(r_t,j)H(j)\), and the Markov-perfect reaction of a rival’s instrument. That machinery exists for discretion, and it takes the non-multiplier variables as its inputs by construction – a planner that re-optimises has discarded the promise it inherited, so the continuation it faces is a function of the genuine states alone.

That is the reset projector \(\mathsf R=\mathrm{diag}(I,0)\) this engine already owns. loose_commitmentize applies exactly it – but only after the fixed point. Inside the coefficient loop, where both channels are formed, they were substituting the raw rule. They now substitute \(H\mathsf R\).

It is a no-op almost everywhere, which is why it went unseen:

indicator

why the reset changes nothing

(0,0) pure discretion

\(H\) already equals \(H\mathsf R\); the multiplier columns of the rule are zero at the fixed point

(1,1) pure commitment

the discretion gate is zero, so the continuation term is never used

Only a mixed indicator reaches the case that matters: a discretion regime substituting forward into a committed successor, whose rule does respond to the promises made today – those columns measure 2.57, not zero.

It is also read only by a player whose differentiation targets include a multiplier. Own-level multipliers – a player’s own and its same-@order peers’ – are never targets at any level, so only a player above the bottom level reads them, and for a single-level Nash game the whole thing is a no-op.

Measured on a forward-looking game with a regime-varying indicator: 0.37 of a leader’s contemporaneous block from the continuation channel, and 1.5e-01 of the solution from the cross-chain. With both reset the two routes agree to 5.3e-09 on a two-player hierarchy and 2.2e-08 on game_fl3lf, which is two leaders sharing a level above one follower – the only shape in which both channels are live on the same player, and the reason the second defect was invisible until that fixture existed.

Nothing in the general route changed, and nothing needed to. Its derivatives_map selects the non-multiplier states because f1942 is discretion machinery; that is correct design, not an omission.

The commitment gate is dated by row

One symbol, two jobs, and the right date depends on which row it sits in.

On a structural row the indicator asks “does the planner honour an inherited promise NOW”. That is a property of \(t\), and it has always been left there.

On a row that is a previously eliminated player’s first-order condition – which exists only under a hierarchy – it asks something else. That condition carries the follower’s promise term, and the block it sits in is multiplied by \(\lambda_{t+1}\), so what a leader prices is

\[\beta\,E_t\!\left[\lambda^{L}_{t+1}\,c_{t+1}\right]\]

– “will my follower, NEXT period, honour the promise it is making now”. The indicator belongs to the regime realised at \(t+1\), inside the expectation. Left undated it was read at \(r_t\), and being a bare parameter it had no tag for the forward shift to move: the same mechanism as the dc/du^rival defect above, worth 0.5 of the leader’s lead block.

date_switching_parameters now splits by row. Rows above the structural block get the indicator dated at \(t+1\); the structural rows keep it at \(t\). Splitting by row is what makes this safe – a single planner and a same-level Nash game have no rows above the structural block, so neither can be reached at all. The fix applies to the general route too, which was mis-dating leader-follower models with a switching indicator whichever solver was chosen.

A note on retcode 25

This engine’s answer goes through RISE’s ordinary mean-square-stability check, so a model with no stable equilibrium comes back as retcode 25.

That is worth stating because until recently the general route did not report it. +solve/solve.m switches solve_check_stability off for the duration of the grand replanning loop and restores it afterwards, but the post-loop call to is_stable_system sat between the loop and the restore – and that function short-circuits to “stable” whenever the flag is false. The check was therefore inert on every discretion, loose-commitment and replanning solve routed through that loop.

It was found by diffing the two routes on a two-player game with a commitment regime: both returned the same solution, six significant figures apart, is_stable_system rejected it, and only the linear-quadratic route said so. The restore now happens before the call. Models previously reported as solved may now return retcode 25 – correctly.

If you hit it, the model has no mean-square-stable equilibrium at that calibration; it is not a solver failure.

What it buys

On a two-country central-bank game with 34 endogenous variables and two regimes, under discretion with Markov-perfect play, the specialised route is between 8 and 9 times faster than the general route (4.4-4.9 s against 39-40 s) and agrees with it to 1.5e-06, which is the general route’s own accuracy. It also succeeds at default settings where the general route returns retcode 210 and needs solve_discretion_maxiter lifted from 100 to 500.

On small fixtures the advantage disappears – warm, the two routes are within a factor of two of each other at 13-17 equations, because fixed costs dominate. The gain is a function of model size, which is the point: it is what makes estimation and repeated solution feasible.

Validation lives in models/dsge/optimal_policy/lq_games in the test suite, which compares the two routes on backward- and forward-looking games, one and two regimes, two and three players, and both equilibrium concepts.

1.8.17. A worked example: Tatiana’s monetary-fiscal game

The smallest fixture that exercises every (policy, solve_policy_type, solve_policy_equilibrium) cell is Tatiana’s monetary-fiscal game, distributed with the regression suite. It declares four policy branches behind a rise_flags switch – cooperative, nash, M_leader, F_leader – without pinning commitment or the equilibrium concept, so the {ramsey, discretion} x {OLE, MPE} cross-product is selectable on the model object. With four policy branches this yields a 4 x 2 x 2 = 16-cell regression cube.

The model is a three-equation linearised monetary-fiscal model with five endogenous variables (output gap, inflation, the policy rate, a fiscal instrument, government debt), three shocks, and the two players’ objectives written out separately. See the legacy chapter Optimal Policy for the full tatiana.rs listing and its driver, test_tatiana_policy_equilibria.m.

1.8.18. Nonstationary optimal policy: geometric multipliers

On a nonstationary model solved in levels (solve_bgp), the planner’s Lagrange multipliers grow geometrically along the balanced growth path, at heterogeneous rates, and are sign-indefinite – some negative, some exactly zero. The additive steady-state/growth encoding used for level variables cannot represent that, which historically left a structural residual floor on every nonstationary optimal-policy model.

The machinery that handles it is strictly opt-in at parse time:

m = rise('mymodel', 'geometric_multipliers', true);
m = set(m, 'solve_bgp', true, ...);
[m, ~, retcode] = sstate(m);   % balanced growth path, multipliers included

Under the option, the multiplier steady-state leads/lags are encoded multiplicatively (lead ss*g, lag ss/g, growth slots holding gross factors with neutral value 1), the BGP shift moves multipliers along the geometric ray level*g^K, and the steady-state stage solves each multiplier group’s levels and growth factors jointly (minimum-norm on the two-point reference/shifted FOC system – the multiplier steady state is only a point on the BGP, so the group Jacobian is singular by construction).

On a model whose multipliers grow, the option carries the steady state, not the dynamics: solve stops with rise:geometric_multipliers:growingWithoutCharts. The perturbation coefficients of a multiplier that grows geometrically drift with the trend, so in multiplier levels there is no time-invariant solution to return, and RISE refuses rather than return a wrong one. The steady state stays available through sstate. For the dynamics, parse with the multiplier charts below, or solve the detrended model. When no multiplier grows (a trend with zero drift, or a stationary model), the solve goes through without the charts.

Leave the option off (the default) for stationary models. It is not needed there, and it changes nothing: commitment, discretion, loose commitment and stochastic replanning give the same solution with it on or off. Like no_u_turn, this is a declared user intention – it is never inferred from the model.

The power-scaling multiplier charts (multiplier_charts)

On top of geometric_multipliers, the parse option multiplier_charts (with multiplier_numeraire, the name of one log-declared nonstationary variable) wraps every multiplier in the dynamic equations as MU_i = ZNUM^{B_i} * mu_i. After the balanced-growth solve the exponents B_i are filled in automatically, so the scaled multipliers are stationary signed levels: there is no sign classification and no zero guard, and negative and exactly-zero multipliers work natively. The static system stays chart-free by construction, so the balanced growth path is the same with and without the charts, and weakly-identified growths are frozen to the identity chart with a loud warning. This is the way to the dynamics of a nonstationary Ramsey model in levels:

m = rise('mymodel', 'geometric_multipliers', true, ...
         'multiplier_charts', true, 'multiplier_numeraire', 'A');
[m, retcode] = solve(m, 'solve_bgp', true, ...);

The numeraire must be nonstationary: a stationary one stops the classification with an error, and a model whose multipliers do not grow needs no charts. The nonstationary_multipliers unit tests lock the balanced growth path against its closed forms, the refusal, and the charted solution, whose first- and second-order responses match those of the detrended model.

Worked example: rise-modern-tutorials/OptimalPolicy/NonstationaryRamsey.

Exact exponents. Along a balanced growth path driven by independent trends Z_1, ..., Z_k, every multiplier grows as an exact power of the trends, MU_i ~ Z_1^{b_i1} * ... * Z_k^{b_ik}. The exponent vector is a property of the equations. It is the homogeneity of the Lagrangian: roughly, a multiplier scales like the objective divided by its constraint. RISE computes it from the symmetry of the static system, not from the numerically solved growth factors. As a result:

  • Exponents such as -1 or 0 come out exactly, and a multiplier counts as trend-free only when its exponent vector is exactly zero.

  • A zero, tiny or negative multiplier gets its exponent from its first-order condition, never from its level.

  • The classification does not change when the balanced-growth solve is perturbed.

The result is stored in m.model_data.diagnostics.multiplier_trends, with fields trend_names, names, B, non_geometric and reason.

Several independent trends. A model can have more than one independent trend, for example technology A and the price level P. Pass one numeraire per trend:

m = rise('mymodel', 'geometric_multipliers', true, ...
         'multiplier_charts', true, 'multiplier_numeraire', {'A','P'});

Each multiplier is then wrapped as exp(B_i_A*A + B_i_P*P) * mu_i, with parameters B_<multiplier>_<numeraire>. Every scaled multiplier is then invariant to each trend separately, not only along the balanced growth path. With a single numeraire, the parameters keep their names B_<multiplier>.

If you pass fewer numeraires than there are independent trends, a multiplier whose trend the numeraires do not span gets a chart fitted to the balanced growth path. The perturbation is still time-invariant, but the scaled multiplier is not invariant to independent trend shocks, and the warning rise:multiplier_charts:notSpanned names the multipliers and the numeraires to add.

Limits.

  • Non-geometric multipliers. If the objective contains the log of a trending variable multiplied by a shock (for example ZETA*log(C)), the multiplier on that shock’s law of motion grows arithmetically. No power chart makes it stationary. It is detected and reported (rise:multiplier_charts:nonGeometric). Such a multiplier is a recorder that does not feed back into the allocation. Write the objective in detrended form (ZETA*log(C/A), which differs by a term in exogenous variables only) to remove it.

  • When the exponents cannot be computed exactly. This happens when the first-order conditions depend on the policy function (discretion, loose commitment), when the static equations are not smooth (abs, max), or when the trend symmetry cannot be identified cleanly. With a single numeraire, RISE then falls back to exponents inferred from the solved growth factors, with the warning rise:multiplier_charts:exactDeclined and the reason. With several numeraires this is an error.

1.8.19. Where to look next

  • Optimal Policy (legacy DSGE chapter) – the long-form reference: full @optimization_problem grammar with every bracket option, the multiple-policymakers section, the MPE derivation and its equivalence with the alternative value-function-gradient formulation, the loose-commitment and stochastic-replanning treatment, and the Tatiana worked example. Canonical for the modern toolbox; folding pending review.

  • Optimized simple rules – the alternative that keeps the policy equation and only picks its coefficients optimally.

  • Solving – the option surface for solver, solve_policy_type, and solve_policy_equilibrium, plus the diagnostic protocol when the solver returns retcode 21.

  • Model file language (Optimal policy section) – the grammar reference for @optimization_problem, single-player and multi-player.

  • Time-varying transition probabilities – when the commitment chain’s transitions are endogenous.