Time Indices in Dolo Function Compilation¶
Overview¶
Time indices ([t], [t+1], [t-1]) in dolo equations serve as a declarative specification of which time periods' data a function needs. During compilation, these indices are transformed into positional arguments in the generated functions. This document explains how this transformation works and why it matters for understanding dolo's architecture.
Time Index Meanings¶
[t-1]or-1: Previous period (past)[t]or0: Current period (present)[t+1]or1: Next period (future)
The Compilation Process¶
1. Starting Point: YAML Equations¶
equations:
transition:
- k[t] = (1-delta)*k[t-1] + i[t-1]
arbitrage:
- 1 - beta*(c[t]/c[t+1])^(sigma)*(1-delta+rk[t+1])
2. Recipe-Driven Signature Generation¶
The recipes.yaml file defines which time periods each equation type needs:
arbitrage:
eqs:
- ['states', 0, 's'] # Current states (t)
- ['controls', 0, 'x'] # Current controls (t)
- ['states', 1, 'S'] # Future states (t+1)
- ['controls', 1, 'X'] # Future controls (t+1)
- ['parameters', 0, 'p'] # Parameters (timeless)
transition:
eqs:
- ['states', -1, 's'] # Past states (t-1)
- ['controls', -1, 'x'] # Past controls (t-1)
- ['states', 0, 'S'] # Current states (t) - output
3. The Transformation Pipeline¶
Step 1: Parse with Time Information¶
# Original equation
"k[t] = (1-delta)*k[t-1] + i[t-1]"
# Parsed AST preserves timing
AST: Assignment(
Variable('k', 0),
Add(
Mult(Sub(1, 'delta'), Variable('k', -1)),
Variable('i', -1)
)
)
Step 2: Remove Timing for Calibration¶
Step 3: Apply Time Shifts¶
# factories.py line 117
eqs = [time_shift(eq, tshift) for eq in eqs]
# This transforms variable names based on context:
# If processing for time t:
"k = (1-delta)*k__m1 + i__m1"
Step 4: Map to Function Arguments¶
# Based on recipes.yaml specification
arguments = {
's': ['k'], # From ['states', -1, 's']
'x': ['i'], # From ['controls', -1, 'x']
'S': ['k'], # From ['states', 0, 'S'] (output)
'p': ['delta'] # From ['parameters', 0, 'p']
}
Step 5: Generated Function¶
def transition(s, x, p):
# Inside, k[t-1] → s[0] (first element of states array)
# i[t-1] → x[0] (first element of controls array)
# k[t] → output
k = (1-p.delta)*s[0] + x[0]
return np.array([k])
Key Insight: Time Indices as Argument Mapping¶
Time indices don't exist in the compiled function. They're a compile-time directive that determines:
- Which arrays the function receives as arguments
- The order of these arguments
- How variables map to array positions
Example: Arbitrage Function¶
Compiles to:
def arbitrage(m_t, s_t, x_t, m_tp1, s_tp1, x_tp1, p):
# c[t] comes from x_t array
# c[t+1] comes from x_tp1 array
# rk[t+1] is computed from s_tp1, x_tp1
c = x_t[0] # Current consumption
c_next = x_tp1[0] # Future consumption
rk_next = compute_rk(s_tp1, x_tp1, p)
return 1 - p.beta*(c/c_next)**(p.sigma)*(1-p.delta+rk_next)
The tshift Parameter¶
The get_factory() function accepts a tshift parameter that shifts all time indices:
# Normal (tshift=0): k[t-1] → past, k[t] → current
# Shifted (tshift=1): k[t-1] → current, k[t] → future
This is useful for: - Creating lagged versions of functions - Adjusting reference frames for different algorithms - Generating derivative functions at different time points
Implications for ADC Models¶
Understanding time indices is crucial for the ADC extension because:
- ADC models are timeless at the stage level
- Time indices create cross-stage coupling
- Removing time shifts (setting
tshift=0always) is key to modularity
In the proposed ADC extension:
# Check for ADC equations
adc_equations = ['g_av', 'g_ve', 'q_function', 'foc_adc']
if eq_type in adc_equations:
tshift = 0 # Force no time shift
This ensures ADC transition functions remain pure state-to-state mappings without temporal coupling.
Summary¶
Time indices in dolo serve as a declarative specification that gets compiled away:
- Before compilation:
k[t-1]indicates "I need past k" - During compilation: Maps to specific function argument
- After compilation: Just
args[0][0](first state, first variable)
They're not time travel - they're instructions for the compiler about data organization. This understanding is essential for extending dolo to support timeless, modular representations like ADC.