The dolo language
The easiest way to code a model in dolo consists in using specialized Yaml files also referred to as dolo model files.
YAML format
YAML stands for Yet Another Markup Language. It is a serialization language that allows complex data structures in a human-readable way. Atomic elements are floats, integers and strings. An ordered list can be defined by separating elements with commas and enclosing them with squere brackets:
[1,2,3]
Equivalently, it can be done on several line, by prepending [-]{.title-ref} to each line
- 'element'
- element # quotes are optional there is no ambiguity
- third element # this is interpreted as ``'third element'``
Associative arrays map keys to (simple strings to arbitrary values) as in the following example:
{age: 18, name: peter}
Mappings can also be defined on severaly lines, and as in Python, structures can be nested by using indentation (use spaces no tabs):
age: 18
name: peter
occupations:
- school
- guitar
friends:
paula: {age: 18}
The correspondance between the yaml definition and the resulting Python object is very transparent. YAML mappings and lists are converted to Python dictionaries and lists respectiveley.
Note
TODO say something about YAML objects
Any model file must be syntactically correct in the Yaml sense, before the content is analysed further. More information about the YAML syntax can be found on the YAML website, especially from the language specification.
Example
Here is an example model contained in the file
examples\models\rbc.yaml
name: Real Business Cycle
symbols:
exogenous: [e_z]
states: [z, k]
controls: [n, i]
parameters: [beta, sigma, eta, chi, delta, alpha, rho, zbar, sig_z]
definitions: |
y[t] = exp(z[t])*k[t]^alpha*n[t]^(1-alpha)
c[t] = y[t] - i[t]
rk[t] = alpha*y[t]/k[t]
w[t] = (1-alpha)*y[t]/n[t]
equations:
arbitrage: |
chi*n[t]^eta*c[t]^sigma - w[t] ⟂ 0.0 <= n[t] <= inf
1 - beta*(c[t]/c[t+1])^(sigma)*(1-delta+rk[t+1]) ⟂ -inf <= i[t] <= inf
transition: |
z[t] = rho*z[t-1] + e_z
k[t] = (1-delta)*k[t-1] + i[t-1]
calibration:
# parameters
beta : 0.99
phi: 1
delta : 0.025
alpha : 0.33
rho : 0.8
sigma: 5
eta: 1
sig_z: 0.016
zbar: 0
chi : w/c^sigma/n^eta
c_i: 1.5
c_y: 0.5
e_z: 0.0
n: 0.33
z: zbar
rk: 1/beta-1+delta
w: (1-alpha)*exp(z)*(k/n)^(alpha)
k: n/(rk/alpha)^(1/(1-alpha))
y: exp(z)*k^alpha*n^(1-alpha)
i: delta*k
c: y - i
V: log(c)/(1-beta)
u: c^(1-sigma)/(1-sigma) - chi*n^(1+eta)/(1+eta)
m: beta/c^sigma*(1-delta+rk)
kss: 10
exogenous: !UNormal
sigma: 0.01
domain:
z: [-2*sig_z/(1-rho^2)^0.5, 2*sig_z/(1-rho^2)^0.5]
k: [ kss*0.5, kss*1.5]
options:
grid: !Cartesian
n: [100, 100]
discrete_choices: [n]
This model can be loaded using the command:
model = yaml_import(`examples/models/rbc.yaml`)
The function yaml_import
(cross) will raise errors until
the model satisfies basic compliance tests. . In the
following subsections, we describe the various syntactic rules prevailing
while writing yaml files.
Sections
A dolo model consists in the following 4 or 5 parts:
- a
symbols
section where all symbols used in the model must be defined - an
equations
section containing the list of equations - a
calibration
section providing numeric values for the symbols - a
domain
section, with the information about the solution domain - an
options
section containing additional informations - an
exogenous
section where exogenous shocks are defined.
These section have context dependent rules. We now review each of them in detail:
Declaration section
This section is introduced by the symbols
]{.title-ref}` keyword. All
symbols appearing in the model must be defined there.
Symbols must be valid Python identifiers (alphanumeric not beginning
with a number) and are case sensitive. Greek letters are recognized. Subscripts and
superscripts can be denoted by _
and __
respectively. For instance beta_i_1__d
will be pretty
printed as \beta_{i,1}^d. Unicode characters are accepted too, as long as they are valid, when used within python identifiers.
Note
In most modern text editor, greek characters can be typed, by entering their latex representation (like beta
) and pressing Tab.
Symbols are sorted by type as in the following example:
symbols:
states: [a, b]
controls: [u, v]
exogenous: [e]
parameters: [rho]
Note that each type of symbol is associated with a symbol list (like [a,b]
).
Alert
A common mistake consists in forgetting the commas, and use spaces only inside list. This doesn't work since the space will be ignored and the two symbols recognized as one.
The exact list of symbols to declare depends on which algorithm is meant to be used. In general, one needs to supply at least states (endogenous states), exogenous (for exogenous shocks), controls for decision variables, and parameters for scalar parameters, that the model can depend on.
Declaration of equations
The equations
section contains blocks of equations sorted by type.
Expressions follow (roughly) the Dynare conventions. Common arithmetic
operators ([+,-,*,/,\^]{.title-ref}) are allowed with conventional
priorities as well as usual functions (sqrt
, log
, exp
, sin
, cos
, tan
,
asin
, acos
, atan
, sinh
, cosh
, tanh
, asinh
, acosh
, atanh
).
The definitions of these functions match the definitions from the
numpy
package.
All symbols appearing in an expression must
either be declared in the symbols
section or be one of the predefined functions. Parameters (that are time invariant) are ntot subscripted, while all other symbol types, are variables, indexed by time. A variable v
appear as v[t-1]
(for v_{t-1}), v[t]
(for v_t),
or v[t+1]
(for v_t).
All equations are implicitly enclosed by the expectation operator E_t\left[\cdots \right]. Consequently, the law of motion for the capital
is written (in a transition
section) as:
k[t] = (1-δ)*k[t-1] + i[t-1]
while the Euler
would be written (in the arbitrage
section) as:
β*(c[t]/c[t+1])^(σ)*(1-δ+r[t+1]) - 1 # note that expectiation is operator
Note
In Python, the exponent operator is denoted by [**]{.title-ref} while the caret operator [\^]{.title-ref} represents bitwise XOR. In dolo models, we ignore this distinction and interpret both as an exponent.
Note
The default evaluator in dolo preserves the evaluation order. Thus
(c[t+1]/c[t])^(-gamma)
is not evaluated in the same way (and is numerically
more stable) than c(1)^(-gamma)/c^(-gamma)
. Currently, this is not
true for symbolically computed derivatives, as expressions are
automatically simplified, implying that execution order is not
guaranteed. This impacts only higher order perturbations.
An equation can consist of one expression, or two expressions separated by [=]{.title-ref}. There are two types of equation blocks:
- Condition blocks: in these blocks, each equation
lhs = rhs
define the scalar value(rhs)-(lhs)
. A list of of such equations, i.e a block, defines a multivariate function of the appearing symbols. Certain condition blocks, can be associated with complementarity conditions separated by⟂
(or|
) as inrhs-lhs ⟂ 0 < x < 1
. In this case it is advised to omit the equal sign in order to make it easier to interpret the complementarity. Also, when complementarity conditions are used, the ordering of variables appearing in the complementarities must match the declaration order (more in section Y). - Definition blocks: the differ from condition blocks in that they define a group of variables (
states
orauxiliaries
) as a function of the right hand side.
The types of variables appearing on the right hand side depend on the block type. The variables enumerated on the left hand-side must appear in the declaration order.
Note
In the RBC example, the definitions
block defines variables (y,c,rk,w
)
that can be directly deduced from the states and the controls:
definitions:
- y[t] = z[t]*k[t]^alpha*n[t]^(1-alpha)
- c[t] = y[t] - i[t]
- rk[t] = alpha*y[t]/k[t]
- w[t] = (1-alpha)*y[t]/w[t]
Note that the declaration order matches the order in which variables
appear on the left hand side. Also, these variables are defined
recursively: c
, rk
and w
depend on the value for y
. In contrast
to the calibration block, the definition order matters. Assuming that
variables where listed as (c,y,rk,w
) the following block would provide
incorrect result since y
is not known when c
is evaluated.
definitions:
- c[t] = y[t] - i[t]
- y[t] = z[t]*k[t]^alpha*n[t]^(1-alpha)
- rk[t] = alpha*y[t]/k[t]
- w[t] = (1-alpha)*y[t]/w[t]
Calibration section
The role of the calibration section consists in providing values for the parameters and the variables. The calibration of all parameters appearing in the equation is of course strictly necessary while the the calibration of other types of variables is useful to define the steady-state or an initial guess to the steady-state.
The calibrated values are also substituted in other sections, including
exgogenous
and options
sections. This is
particularly useful to make the covariance matrix depend on model
parameters, or to adapt the state-space to the model's calibration.
The calibration is given by an associative dictionary mapping symbols to define with values. The values can be either a scalar or an expression. All symbols are treated in the same way, and values can depend upon each other as long as there is a way to resolve them recursively.
In particular, it is possible to define a parameter in order to target a
special value of an endogenous variable at the steady-state. This is
done in the RBC example where steady-state labour is targeted with
n: 0.33
and the parameter phi
calibrated so that the optimal labour
supply equation holds at the steady-state (chi: w/c^sigma/n^eta
).
All symbols that are defined in the [symbols]{.title-ref} section but do not appear in the calibration section are initialized with the value [nan]{.title-ref} without issuing any warning.
Note
No clear long term policy has been established yet about how to deal with undeclared symbols in the calibration section. Avoid them. TODO: reevaluate
Domain section
The domain section contains boundaries for each endogenous state as in the following example:
domain:
k: [0.5*k, 2*k]
z: [-σ_z*3, σ_z*3]
Note
In the above example, values can refer to the calibration dictionary. Hence, 0.5 k
means 50%
of steady-state k
. Keys, are not replaced.
Exogenous shocks specification
Alert
This section is out-of-date. Syntax has changed. Many more shocks options are allowed. See processes for a more recent description of the shocks.
TODO: redo
The type of exogenous shock associated to a model determines the kind of decision rule, whih will be obtained by the solvers. Shocks can pertain to one of the following categories: continuous i.i.d. shocks (Normal law), continous autocorrelated process (VAR1 process) or a discrete markov chain. The type of the shock is specified using yaml type annotations (starting with exclamation mark) The exogenous shock section can refer to parameters specified in the calibration section. Here are some examples for each type of shock:
Normal
For Dynare and continuous-states models, one has to specifiy a
multivariate distribution of the i.i.d. process for the vector of
exogenous variaibles (otherwise they are assumed to be constantly equal to zero). This is done
in the exogenous
section. A gaussian distrubution (only one supported
so far), is specified by supplying the covariance matrix as a list of
list as in the following example.
exogenous: !Normal:
Sigma: [ [sigma_1, 0.0],
[0.0, sigma_2] ]
Alert
The shocks syntax is currently rather unforgiving. Normal shocks expect
a covariance matrix (i.e. a list of list) and the keyword is Sigma
not sigma
.
Markov chains
Markov chains are constructed by providing a list of nodes and a transition matrix.
exogenous: !MarkovChain
values: [[-0.01, 0.1],[0.01, 0.1]]
transitions: [[0.9, 0.1], [0.1, 0.9]]
It is also possible to combine markov chains together.
exogenous: !Product
- !MarkovChain
values: [[-0.01, 0.1],[0.01, 0.1]]
transitions: [[0.9, 0.1], [0.1, 0.9]]
- !MarkovChain
values: [[-0.01, 0.1],[0.01, 0.1]]
transitions: [[0.9, 0.1], [0.1, 0.9]]
Options
The [options]{.title-ref} section contains all informations necessary to solve the model. It can also contain arbitrary additional informations. The section follows the mini-language convention, with all calibrated values replaced by scalars and all keywords allowed.
Global solutions require the definition of an approximation space. The lower, upper bounds and approximation orders (number of nodes in each dimension) are defined as in the following example:
options:
grid: !Cartesian
n: [10, 50]
arbitrary_information: 42