Terminology
0. Setup¶
We proceed to formally define a declaritive model by constructing a sequence of stages.
The DDSL consists of a set of objects, \(\mathbb{O}\), and a set of functional expressions. A subset of the space of objects are the primitive numerical objects, denoted by \(\mathbb{O}_{P}\). The primitive objects are closed under union and intersection. The space of functional expressions are programmable operatioosn on the space of fucntional expresions and primitive objections to the space of functional expressions and primitive objects. We wqill use the term the meaning of an object to to refer to its object programmable function counterpart (in whatever language we are using to implement the model).
primitive objects for now consist of symbols represetnating mathematical spaces, primitive functions. digits, as well as nmerical methods with a ! and numerical schemes.
A syntactic stage \(\mathbb{S} = (\mathbb{F}, \mathbb{C})\) is a set of objects. There are two types of objects within a state: - 1) function objects \(\mathbb{F}\). Each \(\mathbb{f}\in \mathbb{F}\) is a sequence of symbols. Symbols can be decorators, primitive functional objects, arguments (variables) or parameter declarations - 2) a calibration functor \(\mathbb{C}\) that maps parameter declarations to \(\mathbb{O}_{P}\).
For every symbol \(\mathcal{s}\) in \(\mathcal{f}\subset\mathbb{F}\),where \(\mathcal{f}\) denotes a sequence, the calibration functor \(\mathbb{C}\) returns a primitive numerical object, thus \(\mathbb{C}\mathcal{s} \in \mathbb{O}_{P}\). We will say that the stage is calibrated and denote by \(\hat{\mathbb{S}} = (\hat{\mathbb{F}}, \mathbb{C})\) if:
- For every \(\hat{\mathcal{f}}\subset \hat{\mathbb{F}}\),
- There also exists an invertible mathematical meaning map \(\Upsilon : \hat{\mathbb{F}} \to \mathbb{DM}\) that maps the set of function objects to the space of Bellman problems and must define a functional operation \(\mathcal{T} = \mathcal{S}_{1}\circ\dots\circ \mathcal{S}_{K}\), where \((\mathcal{S}_{K})\) are each compact functional operators and represent a valid fatorization of the stage problem.
- There exists a sequence of \((\mathbb{\hat{f}^{m}_{i}})\subset \hat{\mathbb{F}}\) such that \(\Upsilon(\mathbb{\hat{f}^{m}_{i}}) = \mathcal{S}_{i}\). Each \(\mathbb{\hat{f}^{m}_{i}}\) is a functional operator which we call a mover.
- There exists a meaning map \(\measuredangle\) from \(\hat{F}\) into the the space of functional expressions such that for any object \(o\in \mathbb{O}_{P}\), \(\measuredangle\mathbb{\hat{f}^{m}_{i}}o = \Upsilon\mathbb{\hat{f}^{m}_{i}}o = \mathcal{S}_{i}o\).
Criteria 4. tells us that the meaning of a calibrated mover must be the same as its mathematical meaning.
Syntax of \(\mathbb{F}\)¶
\(\mathbb{F}\) consists of two sets: - A set of symbols \(\mathbb{SYM}\), where the meaning of each symbol is its type. - A set of perches, \(\mathbb{P}_{l}\), each perch constains a sequence of symbols \(\mathbb{SYM}\) that can be composed to define each \(\mathcal{f}^{m}_{l}\).
Note I leave open the possibility here that new functions are defined in each perch, but they must compose to define the uncalibrated movers.
Moreover, for concenvenicen,w e may split the yamlinto a seperate symbols block and a perches block.
WE HAVE TO HERE DISCUSS METHODS AND SCHEMES. I WANT TO THIN OF GRIDS AND METHODS SOMWEHOW AS FUNCTIONS THAT HAVE PARAMETERS (GRID SIZE ETC.) RATHER THAN PARAMETERS THEMSELVES. THAT IS, the meaning map will take care of the construction of the grids, but the parmaeters must be calibrated at the calbiration stage.
The calibiration file then sits as a seperate file. But each unique stage must be a pair of \(\mathbb{F}\) and \(\mathbb{C}\). We forget about precidence to avoid confusion, what is in the paired calibration file is the single source of truth.
1. Three layers: syntax, math meaning, numerical meaning¶
Think of every DDSL model as living in three layers:
-
Syntactic layer (the YAML file)
-
Names, blocks, decorators, expressions.
-
E.g.
Xa @def R+,Ve: xe @mapsto ve @via | ve = .... -
Mathematical meaning layer
-
Each symbol / expression has an abstract mathematical denotation:
- spaces, functions, operators, schemes…
- This is your “syntax → mathematical object” map.
-
Numerical meaning layer
-
Each numerically relevant symbol / expression also has a concrete numerical denotation:
- grids, arrays, discrete operators, method implementations…
- This is your “syntax → numerical object” map – your numerical_meaning function, realised as a lookup table.
DDSL will specify the structure and contracts of (2) and (3), but it will not prescribe the actual implementation of the algorithms in (3). That’s for the RCK / solver.
2. Syntactic layer (quick recap + how it ties in)¶
You already have most of this:
- Block: top‑level section of the YAML (e.g.
symbols,stage_metadata,equations,calibration,grids). - Sub‑block: nested section inside a block (e.g.
perches.arvl,perches.dcsn). - Perch: a special sub‑block that attaches a local modeling context (arrival/decision/continuation). Semantically, a perch corresponds to an information set (a filtration step): what is known/observable/conditionable at that point; mover bodies must respect this measurability.
- Expression: anything using
@mapsto,@via, decorators like@expectation{},@simulate{},@def, etc. -
Symbol: a named thing in the syntax (e.g.
Xa,Ve,Q_kernel,!gauss-hermite). -
Each symbol has:
- a label (the YAML key, e.g.
square_function), - a symbol name (e.g.
f), - and a kind (space, function, constant, scheme, method, etc.).
- a label (the YAML key, e.g.
Decorator note (your @mapsto, @def, @via, @expectation, …):
they live purely at the syntactic level and tell the math/numerical meaning functions how to interpret the expression.
2.1 Instantiation layer (periods/models): nest, twisters, connectors¶
These terms refer to the host-language orchestration layer that turns timeless templates into a finite-horizon (possibly non-stationary) instantiated model. See spec_0.1h — Period and nests instantiation.
-
Template library / syntax collection: a directory of reusable YAML templates (stages + period templates + shared methodization/settings/calibration templates). Builders read from this library.
-
Stage template: YAML file describing a stage’s symbols/perches/equations. Must be timeless (no time index).
-
Syntactic stage: parsed in-memory stage object obtained from a stage template before binding (currently
dolo.compiler.model.SymbolicModel). -
Bound stage: a syntactic stage with functor attachments:
methodize(methods),configure(settings),-
calibrate(parameter values). Attachments are stage-local and immutable for that stage instance. -
Period (namespace): a composition container that gives a common namespace for its stages. A period template is timeless; an instantiated period is a named instance chosen by the builder.
-
Period template: YAML describing an ordered list of stage names and optional within-period
connectors. Timeless. -
Period instance: dict produced by
dolo.compiler.instantiation.instantiate_period(...)with keys likename,template,stages, optionalconnectors, andbindings. -
Interface: the unified concept for composition adapters (wiring). An interface is an injective rename map between the field tuples of two adjacent perches — it witnesses that the exit of one component is isomorphic to the entry of the next. Formally, for stages \(S_1, S_2\) an interface is a bijection \(\tau: X_{\text{cntn}}(S_1) \xrightarrow{\sim} X_{\text{arvl}}(S_2)\) on random-variable identifiers. When the rename is the identity (names already match), the interface is implicit and omitted. Interfaces form a category: stages/periods are objects, interfaces are morphisms, identity interfaces are identities, and composition of rename maps gives morphism composition (see spec 0.1h §3.3.1).
-
Connector (intra-period interface): an interface between stages within a period. Resolves naming mismatches between adjacent stages in the period's stage list. Surface syntax: a rename map keyed by stage occurrence name (see spec 0.1l §5.1). Identity connectors are omitted.
-
Twister (inter-period interface): an interface between periods in a nest. Maps one period's continuation fields to the next period's arrival fields. In sequential nests, twisters are aligned by position (
twisters[i]connectsperiods[i] → periods[i+1]); in DAG nests, twisters carry an explicitto:target (see spec 0.1l §4.5). Time indices/horizon are permitted only at this inter-period layer; stage and period templates remain timeless. -
Terminal period: the last period in the horizon where a boundary condition is applied. Backward accretion starts here.
-
Distance-from-terminal index \(h\): builder-side counter for how many periods have been accreted from terminal; commonly used to index a parameter schedule. Not visible inside stages.
-
Accretion (backward): building the model by starting from the terminal period and repeatedly adding one more earlier period plus its twister.
-
Nest / model dict (runtime representation): the complete instantiated structure of period instances + twisters. In this repo it is currently represented as a plain dict:
-
{"periods": {name: PeriodInstance, ...}, "period_order": [...], "twisters": [Twister, ...]} -
Representation: the concrete host-language container embodying a declarative object (stage/period/nest) without committing to any numerical “how”. In this repo: stages are represented by
SymbolicModel; period instances and nests are represented by plain dicts. -
Builder / orchestrator: host-language code that selects templates, binds position-dependent parameters (age profiles, mortality, etc.), and assembles the nest/model dict. The orchestrator owns the mapping from position (e.g. "distance from terminal") to parameter values.
2.2 Ordering and direction conventions¶
All ordering terminology follows forward time (the direction an agent lives through the model). Backward movers and backward induction travel against this ordering.
Within a stage — perch ordering (forward time):
| Perch | Tag | Symbol | Position |
|---|---|---|---|
| Arrival | [<], [_arvl] |
\(\prec\) | First — precedes the decision |
| Decision | unmarked, [_dcsn] |
— | Middle — the information set where controls are chosen |
| Continuation | [>], [_cntn] |
\(\succ\) | Last — succeeds the decision |
- The preceding perch of the decision perch is the arrival perch (earlier in forward time).
- The succeeding perch of the decision perch is the continuation perch (later in forward time).
- The \(\prec\) / \(\succ\) symbols denote "before / after the decision" in forward time.
Between stages — stage ordering (forward time):
- Predecessor stage: the stage that comes before in the period's stage list (earlier in forward time; closer to the period's arrival interface). Stage \(j{-}1\) is the predecessor of stage \(j\).
- Successor stage: the stage that comes after (later in forward time; closer to the period's continuation interface). Stage \(j{+}1\) is the successor of stage \(j\).
- Two adjacent stages compose when the continuation interface of the predecessor equals the arrival interface of the successor (possibly up to a connector rename). The connector is the interface (morphism) witnessing this composition.
- In a branching period, a branching stage may have multiple successor stages (one per branch). The stage DAG is a partial order; "predecessor/successor" follows the DAG edges in forward time.
Between periods — nest ordering:
- Predecessor period: the period that comes earlier in forward time (lower index in the period list).
- Successor period: the period that comes later in forward time (higher index).
- Two adjacent periods compose when the continuation interface of the predecessor matches the arrival interface of the successor (possibly up to a twister rename). The twister is the interface (morphism) witnessing this composition.
- The distance-from-terminal index \(h\) counts backward: \(h = 0\) is the terminal period, \(h\) increases as you move earlier in time. This is a builder-side index; stages and periods do not see it.
Backward vs forward direction:
| Direction | Operation | Traversal |
|---|---|---|
| Forward (simulation) | Push-forward on distributions; state transitions | Arrival → Decision → Continuation; period \(h\) → period \(h{-}1\) (decreasing \(h\)) |
| Backward (valuation) | Bellman operators; backward movers B, I | Continuation → Decision → Arrival; period \(h{-}1\) → period \(h\) (increasing \(h\)) |
- Backward movers travel against forward time: B maps continuation values to decision values (\(\succ \to\) unmarked), I maps decision values to arrival values (unmarked \(\to \prec\)).
- Forward movers travel with forward time: push-forward on distributions from arrival to continuation.
- Backward accretion builds the nest from terminal (\(h = 0\)) toward the initial period (\(h = H\)), accreting one period at a time.
3. Mathematical meaning: syntax → mathematical objects¶
Here we formalize your syntax_to_mathematical_map.
3.1 Mathematical object universe¶
Define a universe of mathematical objects:
-
Primitive mathematical objects
-
Numbers: ℤ, ℚ, ℝ (or whatever subset we support).
- Booleans: {true, false}.
-
Basic sets: e.g.
R+, finite sets of atoms, product sets, etc. -
Spaces
-
Abstract sets used as domains / codomains.
-
Examples:
Xa– arrival state space,Xv– decision state space,Xe– continuation state space,W– shock space.- Mathematically:
[[Xa]]_mathis a set (e.g.R+with some structure).
-
Functions
-
Maps between spaces (possibly into ℝ).
- E.g.
g_ve: [xv, a] @mapsto xegives a function[[g_ve]]_math : Xv × A → Xe. -
Value functions:
[[Ve]]_math : Xe → ℝ,[[Vv]]_math : Xv → ℝ, etc. -
Constants
-
Parameters:
β,γ,r, etc. as elements of ℝ. -
Settings: bounds, tolerances, etc., also as elements of ℝ or small finite sets.
-
Operators
-
Functional operations on functions: expectation, maximization, interpolation, rootfinding, etc.
-
These correspond to your schemes (families) at the math level:
expectationscheme: maps a function and a measure to a new function.optimizationscheme: maps an objective to argmax and max value, etc.
-
Schemes and methods
-
Scheme (math): a family of operators of a given type.
- e.g. “expectation” as a map
E : (function on W) → ℝ(or → another function). -
Method (math): a specific algorithm inside a scheme, still abstract.
-
!gauss-hermite,!monte-carlo,!brentq, etc. are named symbols of type “method”, with mathematical contracts (e.g. quadrature rule with n nodes).
- e.g. “expectation” as a map
All of this can be captured as a typed universe of mathematical objects.
3.2 Mathematical meaning function¶
Define a function:
[ \llbracket \cdot \rrbracket_{\text{math}} : \text{Expressions} \to \mathcal{O}_{\text{math}} ]
- Input: a DDSL expression (e.g.
Xa @def R+,Ve: xe @mapsto ve @via | ve = ...). - Output: a mathematical object of appropriate kind (space, function, constant, operator, scheme, …).
Examples:
-
Xa @def R+→[[Xa]]_mathis a space object whose underlying set is the non‑negative reals. -
Ve: xe @mapsto ve→[[Ve]]_mathis a functionXe → ℝ. -
Q_kernel: [xv, a] @mapsto qtilde @via | qtilde = u(a) + β * Ve(g_ve(xv, a))→[[Q_kernel]]_math(xv, a) = u(a) + β · [[Ve]]_math([[g_ve]]_math(xv, a)). -
@expectation{over: w}[ Vv(xv) ]→ a composition of the expectation scheme with a function:[[ @expectation{over:w} ]]_mathis an operator taking[[Vv]]_mathand the shock law and returning a new function.
So the formal DDSL spec will:
- list the kinds of mathematical objects,
- define which syntactic forms produce which kinds,
- and define
[[·]]_mathat least up to equivalence (e.g. modulo a.e., etc.).
4. Numerical meaning: syntax → numerical objects (lookup table)¶
Now your new idea: treat the syntactic → numerical mapping as a lookup table.
4.1 Numerical primitives¶
At the numerical layer we restrict to a small set of primitives, as you suggested:
- Scalar numbers: machine‐level approximations (floats, ints).
- Finite sets / index sets: subsets of ℤ used as indices.
- Arrays / tensors: vectors, matrices, higher‑dim tensors over ℝ or ℝ^k.
- Grids: structured collections of nodes (and possibly weights).
-
Primitive methods: callable objects implementing schemes’ methods:
-
e.g. an implementation record for
!gauss-hermite,!monte-carlo.
You can think of the numerical universe as “everything that can be built from integers and finite sets of them, plus primitive methods”.
4.2 Numerical object universe¶
Define a universe of numerical objects:
-
Grid objects
-
E.g. a record:
(nodes, weights, metadata). -
Example:
[[Xa]]_numis the grid for spaceXa. -
Discrete functions
-
Values on a grid: array of size
norn × k. -
Example:
[[Ve]]_numis an array of Ve values onXe’s grid, plus perhaps an interpolation operator. -
Numerical operators
-
Concrete implementations of functional operators:
- quadrature routine,
- interpolation routine,
- optimizer, rootfinder, etc.
- They may themselves be represented as an object with a callable/dispatch field.
-
Numerical constants / settings
-
Step sizes, tolerances, solver options, etc.
This is structurally very close to the model.num / model.methods picture in StageCraft .
4.3 Numerical meaning function¶
Now define your numerical_meaning function:
[ \llbracket \cdot \rrbracket_{\text{num}} : \text{Expressions} \to \mathcal{O}_{\text{num}} ]
but parameterized by calibration, grids, and method choices. Concretely:
-
Input:
-
A syntactic expression (symbol, function def, grid def, method tag),
- Plus a numerical context: chosen grids, calibration, scheme/method registry.
- Output: a numerical object (grid, array, operator, method implementation…).
This is your lookup table:
- For each symbol
Sthat has numerical meaning, we store:
-
For example:
-
numerical_meaning["Xa"]→ grid forXa. numerical_meaning["Ve"]→ array + interpolation operator for Ve.numerical_meaning["!gauss-hermite"]→ implementation record / callable.
And you are absolutely right: every expression declared in the YAML that participates in computation must eventually get a value in this table if we want to actually run solvers.
Important: [[·]]_num will generally refine [[·]]_math:
- It is only defined for objects whose mathematical type is numerically realizable in the chosen context.
- For example,
[[Xa]]_mathis an infinite set (R+), but[[Xa]]_numis a finite grid approximating it.
So we can think of:
[ \llbracket \cdot \rrbracket_{\text{num}} \approx D_{\text{num}} \circ \llbracket \cdot \rrbracket_{\text{math}} ]
where D_num is a discretization / implementation functor.
5. What DDSL does vs. what the implementation does¶
This directly addresses your point:
the ddsl will go up to defining the functiosn taht return grids, methods etc. but it doesnt define how to implement the actual functions that invbolve composition etc. (ie. the functions int he equatiosn and stage bit)
So:
-
DDSL spec:
-
Defines the syntactic categories (blocks, perches, expressions, decorators).
- Defines the mathematical object types (spaces, functions, operators, schemes, methods).
- Defines
[[·]]_mathfor all expressions. - Defines the shape of numerical objects and the numerical primitives (integers, finite sets, arrays, primitive methods).
-
Defines what it means for a scheme/method to be valid (its type, preconditions, required options).
-
Reference Computational Kit (RCK) / solver:
-
Chooses grids, methods, and settings → builds a numerical context.
-
Fills the numerical meaning table:
- allocates grids,
- evaluates functions on grids,
- binds operator names to actual algorithms.
- Implements the actual host‑language functions that perform composition, iteration, fixed points, etc.
In other words:
- DDSL stops at declaring that
expectationis a scheme, that!gauss-hermiteis a method for it, and thatVeis a function whose numerical representation is “values on the Xe grid with interpolation”. - The implementation decides how to build the Xe grid, evaluate Ve on it, and run
!gauss-hermiteon those arrays.
6. Minimal primitive sets (tying back to your last sentence)¶
SO it seems we need to ultimately define the set of primite objects as integers and sets of them !
You can make that precise as:
-
Numerical primitive objects:
-
Integers ℤ (for indices, sizes).
- Finite subsets of ℤ (index sets).
- Arrays indexed by these finite sets with entries in ℝ (or machine floats).
- Primitive method objects (which themselves may be represented as records keyed by integers/strings).
Everything else in the numerical layer—grids, discrete functions, linear operators—is built out of these.
On the mathematical side, you have the richer structure (spaces, function spaces, operators), but that’s captured by [[·]]_math and does not need to be implementable verbatim.
If you like, the next iteration could be to write 5–10 bullet‑point definitions like:
- Mathematical object: …
- Numerical object: …
- Mathematical meaning function
[[·]]_math: … - Numerical meaning function
[[·]]_num: …
and drop them straight into the “Foundations” section of the devspec. But conceptually, everything you asked for is now baked into this syntax → math → numerics pipeline.
Computational Representation¶
- of operators (movers)
- of symbolic functions (functions in the equations list)
lets not call this numerical representation