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_typeselects commitment vs discretion;solve_policy_equilibriumselects 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,
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_problemwith@stateblocks, 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 auxiliaryLEAD_1_B{t} = B{t+1}is exempted with the block, and its variableLEAD_1_Bis 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 shockEPSgainsDISTORT_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
@orderbehind you, sincemin_u max_wis 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:
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
@robustnessfor 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'– full commitment; the planner re-optimises att = 0and 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
commitmentswitching parameter and a Markov chain. Callingsolve_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 = truethe cross-chain is zero by construction. If you want MPE, leave@no_u_turnat 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 withrise_1, conditional on the solution of the forward-looking variables.Regime-switching problems use the functional-iteration solver
mfi.When
@no_u_turn = truea linear-quadratic solver is used –loose_commitmentorstochastic_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
@stateblocks, 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 |
|
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
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
|
|
general ( |
|
|---|---|---|---|
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 |
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}\):
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
aat 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_commitmentsolver 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
– 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:
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
@orderdoes 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 |
|---|---|
|
iterations of the coefficient loop, floored at 50 –
identical to |
|
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:
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 |
|---|---|
|
\(H\) already equals \(H\mathsf R\); the multiplier columns of the rule are zero at the fixed point |
|
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
– “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
-1or0come 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 warningrise:multiplier_charts:exactDeclinedand 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_problemgrammar 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, andsolve_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
commitmentchain’s transitions are endogenous.