YAML to Model Conversion¶
Overview¶
The conversion from YAML file to Model object happens through yaml_import() and involves multiple parsing stages.
The YAML Structure¶
Standard YAML Model File¶
symbols:
exogenous: [y]
states: [w]
controls: [c]
expectations: [mr]
poststates: [a]
parameters: [β, γ, σ, ρ, r]
equations:
transition:
- w[t] = exp(y[t]) + (w[t-1]-c[t-1])*r
arbitrage:
- β*(c[t+1]/c[t])^(-γ)*r - 1 | 0.0<=c[t]<=w[t]
# New-style block notation
auxiliary_direct_egm: |
a[t] = w[t] - c[t]
expectation: |
mr[t] = β*(c[t+1])^(-γ)*r
calibration:
β: 0.96
γ: 4.0
σ: 0.1
ρ: 0.0
r: 1.00
w: 1.0
c: 0.9*w # Can use expressions!
domain:
w: [0.01, 4.0]
exogenous: !UNormal
sigma: σ
options:
grid: !Cartesian
orders: [100]
bounds: [[0.0, 4.0]]
Conversion Process¶
Step 1: YAML Import (model_import.py)¶
def yaml_import(fname, check=True):
# 1. Read file or URL
txt = read_file_or_url(fname)
# 2. Parse YAML
data = yaml.compose(txt)
# 3. Add filename for reference
data["filename"] = fname
# 4. Create Model object
return Model(data, check=check)
Step 2: Model Initialization (model.py)¶
class Model:
def __init__(self, data):
self.data = data # Raw YAML data
# Initialize lazy properties
self.__symbols__ = None
self.__equations__ = None
self.__calibration__ = None
self.__functions__ = None
Step 3: Symbol Extraction¶
When model.symbols is first accessed:
@property
def symbols(self):
if self.__symbols__ is None:
symbols = LoosyDict()
# Extract from YAML
for category in self.data["symbols"]:
symbols[category] = self.data["symbols"][category]
self.__symbols__ = symbols
return self.__symbols__
Result:
Step 4: Equation Parsing¶
When model.equations is accessed:
@property
def equations(self):
if self.__equations__ is None:
d = dict()
for eq_name, eq_content in self.data["equations"].items():
# Handle different equation styles
if isinstance(eq_content, yaml.nodes.ScalarNode):
# New style: equation_name: |
# equation content
eqs = parse_string(eq_content, start="assignment_block")
else:
# Old style: equation_name:
# - equation1
# - equation2
eq_list = []
for eq_string in eq_content:
eq = parse_string(eq_string)
eq_list.append(eq)
eqs = eq_list
d[eq_name] = eqs
self.__equations__ = d
return self.__equations__
Step 5: Special Parsing Cases¶
Complementarity Conditions¶
Equations with bounds (arbitrage):
Gets parsed into:
- Main equation: β*(c[t+1]/c[t])^(-γ)*r - 1
- Lower bound: 0.0
- Upper bound: w[t]
Creates three functions:
- arbitrage - The main equation
- arbitrage_lb - Lower bound function
- arbitrage_ub - Upper bound function
Multi-line Blocks¶
Using | for cleaner syntax:
Step 6: Calibration Processing¶
@property
def calibration(self):
if self.__calibration__ is None:
from dolo.compiler.misc import CalibrationDict
# Create special calibration dictionary
calib = CalibrationDict()
for key, value in self.data["calibration"].items():
if isinstance(value, str):
# Parse expressions like "0.9*w"
calib[key] = eval_expression(value, calib)
else:
calib[key] = value
self.__calibration__ = calib
return self.__calibration__
Step 7: Exogenous Process Creation¶
Becomes:
Other process types:
- !VAR1 - AR(1) process
- !MarkovChain - Discrete Markov chain
- !Normal - IID normal
- !MvNormal - Multivariate normal
Parsing Pipeline Details¶
Symbol Timing Notation¶
The parser recognizes time indices:
- x[t] - Current period
- x[t-1] - Previous period
- x[t+1] - Next period
Variable Categories¶
Each variable must be declared in exactly one category:
Expression Evaluation¶
The parser handles:
- Standard math: +, -, *, /, ^
- Functions: exp(), log(), sqrt()
- Greek letters: β, γ, σ
- Time subscripts: [t], [t-1], [t+1]
Validation¶
During parsing, dolo checks: 1. All variables are declared in symbols 2. Time indices are consistent 3. Parameter references exist in calibration 4. Equation syntax is valid
Common Patterns¶
Multiple State Variables¶
symbols:
states: [k, h, z] # capital, human capital, productivity
equations:
transition:
- k[t] = (1-δ)*k[t-1] + i[t-1]
- h[t] = h[t-1] + l[t-1]*η
- z[t] = ρ*z[t-1] + eps[t]
Definitions (Auxiliary Variables)¶
Conditional Equations¶
Error Handling¶
Common parsing errors:
# Undefined variable
"w[t] = y[t] + k[t]" # Error if k not in symbols
# Wrong timing
"w[t] = w[t+2]" # Error: t+2 not supported
# Syntax error
"w[t] = exp(y[t]" # Error: missing closing parenthesis