2.4. Deriving models with rise.microfound

2.4.1. Write the economics, not the algebra

rise.microfound lets you author a DSGE model the way you would write it in a paper: declare the atoms and their roles, state each agent’s optimization problem (period objective, choice variables, named constraints), and RISE derives the first-order conditions symbolically and generates a plain RISE model. It does not own or augment a model object – its output is ordinary model code you can read, solve, and edit. Calibration and solving remain the responsibility of dsge_model.

The builder is a MATLAB handle object with a fluent (chained) interface. It is a model definition tool only: it never takes parameter values and has no solve method.

2.4.2. A first example: the RBC model

p = rise.microfound("RBC");
p.endogenous("c","n","k","w","r","Y","A");
p.exogenous("eps");
p.parameter("beta","delta","psi","eta","alpha","rho");

p.agent("household", discount="beta") ...
    .maximize("log(c) - psi*n^(1+eta)/(1+eta)") ...
    .choose("c","n","k") ...
    .subject_to(budget="c + k = w*n + (1-delta+r)*k{-1}");

p.equilibrium( ...
    production="Y = A*k{-1}^alpha*n^(1-alpha)", ...
    labdemand ="w = (1-alpha)*A*k{-1}^alpha*n^(-alpha)", ...
    capdemand ="r = alpha*A*k{-1}^(alpha-1)*n^(1-alpha)", ...
    tfp       ="A = A{-1}^rho*exp(eps)");

The object can be handed to dsge_model three equivalent ways:

m = dsge_model(p);           % (1) directly
m = dsge_model(code(p));     % (2) via the generated cell array of code lines
rs(p,'rbc.rs'); m = dsge_model('rbc.rs');   % (3) via a written .rs file

Parameter values are passed to dsge_model at solve time, as a structure:

params = struct('beta',0.99,'delta',0.025,'psi',1,'eta',1,'alpha',0.36,'rho',0.95);
m = solve(m, 'parameters', params);
% Number of solutions: 1
% steady state: c=2.555, n=0.928, k=35.24, w=2.371, r=0.0351, Y=3.436, A=1

2.4.3. What is generated

code(p) returns the derived model as a column cell array of code lines; rs(p,file) writes it to a .rs file, and show(p) prints it. For the RBC above the generated model is:

@endogenous c n k w r Y A household_lambda_budget
@exogenous eps
@parameters beta delta psi eta alpha rho
@model
    % production
    Y = A*k{-1}^alpha*n^(1-alpha);
    % labdemand
    w = (1-alpha)*A*k{-1}^alpha*n^(-alpha);
    % capdemand
    r = alpha*A*k{-1}^(alpha-1)*n^(1-alpha);
    % tfp
    A = A{-1}^rho*exp(eps);
    % household: budget
    (c + k)-(w*n + (1-delta+r)*k{-1});
    % household: FOC wrt c
    (1)/(c)+household_lambda_budget*(1);
    % household: FOC wrt n
    -(((real_power(n,1+eta,1))*(psi))*(1+eta)/((1+eta)^(2)))+household_lambda_budget*(-(w));
    % household: FOC wrt k
    household_lambda_budget*(1)+(beta)*household_lambda_budget{+1}*(-(1-delta+r{+1}));

Three conventions to note:

  • Lagrange multipliers are added automatically as endogenous variables, named <agent>_lambda_<constraint-label> (here household_lambda_budget). The labour-supply and Euler equations are the derived FOCs; the budget constraint is emitted as a residual.

  • Equation tags. Every derived equation is preceded by a comment identifying its origin: % <agent>: <constraint-label> for a constraint, % <agent>: FOC wrt <control> for a first-order condition, and the label you gave for an equilibrium equation. These make the generated file self-documenting.

  • No optimization block, nothing hidden. The output is an ordinary RISE model: equilibrium identities, the derived FOCs, the agents’ constraints, and the shock processes. There is no @optimization_problem block.

Note

Steady state. microfound does not compute or require the analytical steady state – providing it would mean hand-deriving exactly the algebra the tool automates. dsge_model forms the steady-state system from the derived FOCs and solves it numerically. You may pass p.steady_state(var=expr,...) with a rough initial guess (a single seed per variable) to help the solver; it is refined, not taken as the solution, and can often be omitted. This mirrors gEcon, which likewise derives and numerically solves the steady-state equations.

2.4.4. Multiple agents

An economy can have several agents; each agent(...) block is differentiated independently and contributes its own FOCs and multipliers. A firm that chooses capital and labour to maximise profit, for instance, is written as another agent:

p.agent("firm") ...
    .maximize("A*k{-1}^alpha*n^(1-alpha) - w*n - r*k{-1}") ...
    .choose("k","n");

The gEcon example corpus (consumer + firm problems, adjustment costs, capital laws of motion, two-sector and two-country variants) is reproduced this way; see the validation note below.

2.4.5. Regime switching

Regime switching enters through switching parameters – and nothing else in how the agent’s problem is written changes. Declare a Markov chain, mark the parameters that switch on it, and the derived FOCs become the correct, probability-weighted switching first-order conditions automatically. This is a capability gEcon does not have.

p = rise.microfound("Switching-preferences RBC");
p.endogenous("c","n","k","w","r","Y","A");
p.exogenous("eps");
p.chain("pref", 2);                       % a 2-state Markov chain named "pref"
p.parameter("beta","delta","eta","alpha","rho");
p.parameter("pref_tp_1_2","pref_tp_2_1"); % chain transition probabilities
p.parameter("psi", switches_on="pref");   % labour disutility switches on "pref"

p.agent("household", discount="beta") ...
    .maximize("log(c) - psi*n^(1+eta)/(1+eta)") ...
    .choose("c","n","k") ...
    .subject_to(budget="c + k = w*n + (1-delta+r)*k{-1}");
% ... equilibrium block unchanged ...

The switching parameter is emitted under a chain-scoped header, @parameters(pref,2) psi, and the labour FOC references psi (which now switches). Switching values and transition probabilities are supplied at solve time with the underscore convention <param>_<chain>_<state> and <chain>_tp_<i>_<j>:

params = struct('beta',0.99,'delta',0.025,'eta',1,'alpha',0.36,'rho',0.95, ...
                'psi_pref_1',1, 'psi_pref_2',3, 'pref_tp_1_2',0.1, 'pref_tp_2_1',0.1);
m = solve(dsge_model(p), 'parameters', params);
% Number of solutions: 1
% regime 1 (psi=1):  n=0.928  c=2.555  k=35.24
% regime 2 (psi=3):  n=0.536  c=1.475  k=20.35

The steady state is solved per regime from the derived switching FOCs: higher labour disutility (psi=3) yields lower hours and capital, as expected.

2.4.6. Output methods

Method

Result

code(p)

the derived model as a column cell array of code lines (the canonical artifact)

rs(p,file)

write the derived model to a .rs file

show(p) / show(p,"agent")

print the derived model (optionally one agent’s block)

latex(p,file)

a LaTeX model appendix (notation + each agent’s problem + derived FOCs)

2.4.7. API reference

Call

Meaning

p = rise.microfound("Title")

create a builder

p.endogenous("x", "y: a description", ...)

declare endogenous variables (inline "name: description" optional)

p.exogenous("eps", ...) / p.shock(...)

declare shocks

p.parameter("beta", ..., switches_on="chain")

declare parameters; switches_on marks a switching parameter

p.chain("name", nstates)

declare a Markov chain

p.agent("name", discount="beta")

open an agent’s problem (returns a fluent handle)

.maximize("U") / .minimize("L")

the period objective

.choose("c","n","k")

the choice (control) variables

.subject_to(label="L = R", ...)

named equality constraints (multiplier <agent>_lambda_<label>)

p.equilibrium(label="eqn", ...)

market-clearing / identity equations

p.steady_state(var="expr", ...)

optional rough initial guess for the numerical steady state

2.4.8. Scope and roadmap

Optimal policy. microfound derives the first-order conditions of private (competitive) agents – it assumes rational expectations with full commitment and does not solve a planner’s (Ramsey) problem. For optimal policy, use RISE’s dedicated optimal-policy facilities (the @optimization_problem planner block); the two share the same underlying FOC engine (rise.engine.foc). A natural workflow is to generate the competitive economy with microfound and add a planner block for the Ramsey analysis.

Heterogeneous agents (HANK). The heterogeneity vocabulary is present in the API – axis(name, points, ...), individual(...), aggregate(...), aggregation(...), and over= on an agent – so that a HANK layer can emit @heterogeneity_axis / @endogenous(individual) / @Agg constructs. In the current release this vocabulary is stored but not yet derived: authoring an individual’s problem over a wealth grid is a planned (phase-2) capability, not a functional one today. Representative-agent and regime-switching models are the supported cases.

2.4.9. Validation

The representative-agent derivation is validated by re-authoring nine gEcon example models in microfound – basic RBC, RBC with installation costs, capacity utilisation, habit formation, monopolistic competition (with a household stochastic discount factor), two sectors, home production, time-to-build, and two countries with risk sharing. All derive and solve, and the two-sector model reproduces gEcon’s own derived steady state. See examples/microfound/ in the toolbox for runnable drivers.