3. Symbol System¶
This section defines the role and typing of symbols in a DDSL model. The symbol system provides the formal structure for declaring and organizing all entities that appear in a model, including the operator registry that enables the methodization functor to identify and transform functional operators.
3.1 Core Principle¶
The fundamental design principle of the DDSL symbol system is:
Every non-primitive symbol that appears anywhere in a model must be declared and typed in a single symbols section. (Built-in primitives/operators/method constructors are implicit.) This declaration assigns a clear mathematical meaning (under Υ) and numerical meaning (under ρ) to the symbol and forms a core part of making the DDSL create a model system that provides a one-to-one map from the math to numerical computations via methodization and calibration.
This ensures complete type safety, clear semantics, and unambiguous interpretation throughout the modeling pipeline.
3.2 Symbol Universe Partition¶
Formally, a model carries a finite sequence of symbol declarations:
Each declaration is an object that associates:
- A descriptive label (human-readable description in quotes)
- A symbol name (the actual identifier used in equations)
- A kind (determined by the indentation heading in the YAML syntax: spaces:, parameters:, variables:, etc.)
- Primitive set-theoretic operations or decorations that convey mathematical meaning (@in, @def, etc.)
- Optionally, additional symbols required by a declaration (e.g., a space constructor like Interval(a_min, a_max) introduces free names that must be declared as parameters/settings)
The general format is: "descriptive label": symbol_name @decorator(s)
We partition the symbol universe into disjoint named sets:
We can think of perches not as a symbol set, but as a subset of SYM that allows us to define each of the movers independently. We can partition the
stagesyntax into a collection of tuples, but this collection does not need to correspond to the SYM partition. The DDSL directives will define the implicit or explicit relationship between the perches and the SYM partition, and between the collection and SYM partitions.
At the syntactic stage, symbols are just typed sets with their kinds determined by their position in the YAML hierarchy and/or a rule in the DDSL directives. Once a representation map has been chosen, each symbol is interpreted as an expression that evaluates into primitive objects.
An important rule within the stage is that the collection of symbols must define a composable set of operations that map to a push-forward and pullback of a Bellman operator.
3.3 Collections of Symbols in a Stage (Sections of YAML)¶
Here I describe how symbols are collected in a stage.
In the syntax, "" will denote a "name" in a string. The first item following the name is a symbol declaration, and the item after that will be a mapping to a primitive object (either a primitive string, a numerical object, or a function object that maps to an AST).
The DDSL organizes symbols into two broad levels, and into collections within each level.
Level 0: Stage¶
We can think of this stage as implicitly defining the Bellman operator and the push-forward operator (but without using Banach space language). However, this should declare the input functions and output functions of the Bellman operator and the push-forward operator, and how they are produced.
stage:
name: consind
model_type: adc_stage
inputs:
- "cntn_vlu": Ve: Xe -> R
- "arvl_dist": D_arvl: Dist(Xa)
outputs:
- "arvl_vlu": Va: Xa -> R
- "dcsn_vlu": Vv: Xv -> R
- "dcsn_dist": D_dcsn: Dist(Xv)
- "cntn_dist": D_cntn: Dist(Xe)
The idea is that the rest of the stage acts like a functor that shows how the input functions and output functions are produced.
Level 1: Backus Functions Mapping to Primitive Objects¶
1.1 Spaces¶
Spaces (\(\mathbf{SPACES}\)) are the basic domains on which all other objects live and are a clear collection or section within the stage.
We denote finite-dimensional spaces, but not infinite-dimensional spaces. The spaces on which the movers operate will be implicit within the DDSL syntax. This is to keep the syntax user-focused.
spaces:
"arvl_state_space": Xa @def R+
"dcsn_state_space": Xv @def R+
"cntn_state_space": Xe @def R+
"shock_support_space": W @def R
"action_space": A @def Interval(a_min, a_max) # note that the "true/ unapproximated" model's action space is here a_min, a_max.
TODO: The above syntax would imply that a_min a_max is a parameter, not a setting, in the model. Is this consistent/correct? An alternative would be to methodize the argmax with bounds.
TODO: Any exogenous state variables should be included in the spaces section so it becomes part of the arvl state-space, etc.
Under \(\Upsilon\), each space maps to a mathematical space (e.g., \(\Upsilon(\text{Xa}) = \mathbb{R}_+\)).
1.2 Variables¶
We declare variables and can use # to add comments giving economic meaning.
Variables (\(\mathbf{VARS}\)) are the basic elements of the spaces:
variables:
states:
"arvl_state": xa @in Xa # beggining of period assets
"dcsn_state": xv @in Xv # wealth
"cntn_state": xe @in Xe # end of period assets
controls:
"consumption": a @in A # consumption
Variables end up as arguments to functionals, but the syntax above is just a declaration of the variables which yield to a representation map to primitive objects.
1.3 Shocks¶
Shocks are actually a subset of (\(\mathbf{DIST}\)):
Note that this is sufficient to define the exogenous shock distribution. If the shock distribution depends on the state, the state variable symbol can be used above to define the shock distribution.
TODO: Consider adding an example above of how this works: i.e. an AR1 process where the exogenous state enters as a state variable.
1.3 Parameters¶
Parameters (\(\mathbf{PARAMS}\)) are the basic elements of the spaces:
Parameters are the basic elements of the model that are fixed for a given calibration, but can vary between calibrations.
TODO: We may consider defining parameters as all symbols that are not variables or shocks. In turn variables are arguments to functionals.
1.4 Primitive Operators¶
We can initially define (or name) operator instances so methodization can attach different methods to them (rather than attaching methods to syntax occurrences/addresses).
operator_table: # WIP name; see also the top-level `operators:` block in §3.4.9
"Income_Expectation": EI @def E
"Port_Expectation": EP @def E
Here we have said that the above two operators are two distinct expectation operators. We have separated them so we can attach separate methods to them. Otherwise we can just use the symbol E for both, but the method will be the same for both.
Level 2: Perches¶
Now we turn to Backus Objects whose representation yields an AST. These will be collected in perches, with each perch defining a forward and backward mover.
First up are functionals (\(\mathbf{FUNCS}\)). These are named real-valued functions that map to primitive numerical objects.
functionals:
"arvl_to_dcsn_transition": g_av: [xa, w] @mapsto xv @via |
xv = xa * r + w
"dcsn_to_cntn_transition": g_ve: [xv, a] @mapsto xe @via |
xe = xv * r + a
"utility_function": u: [a] @mapsto R @via |
u = log(a)
"dcsn_q_function": Q_kernel: [xv, a] @mapsto R @via |
Q_kernel = u(a) + β * E[Vv(g_ve(xv, a))]
We can define a function by also including additional equations within the function declaration; these equations define new variables, but not new functions.
functionals:
"dcsn_q_function": Q_kernel: [xv, a] @mapsto R @via |
Q_kernel = u(a) + β * E[Vv(g_ve(xv, a_tilde))] |
a_tilde = a**2
Scoping rule (WIP but important):
- Names declared in symbols: are stage-global and immutable (single-assignment at stage scope).
- Names introduced inside an @via block are local temporaries (lexically scoped to that @via block), and are not exported or reused across equations/movers.
If you want a reusable stage-level object, declare it explicitly in symbols: (e.g., as a function) and define it once.
Under Υ, functions map to mathematical functions on the corresponding spaces. Thus, this is why we do not declare the symbols g_ve and so on by defining their domain/codomain, etc.
Under Υ, distributions map to probability measures on the corresponding spaces.
perches:
# Sequence defines computational flow
sequence:
forward: [arrival, decision]
backward: [decision, arrival]
arrival:
backward:
B_arvl: Vv -> Va @via |
xv = g_av(xa, w)
Va(xa) = E_w[Vv(xv)]
forward:
F_arvl: D_arvl -> D_dcsn @via |
xa_tilde ~ D_arvl
w ~ D_w
xv_tilde = g_av(xa_tilde, w)
decision:
backward:
B_dcsn: Ve -> [Vv, astar] @via |
astar(xv) = argmax_{a in Gamma(xv)} Q_kernel(xv, a)
Vv(xv) = Q_kernel(xv, astar(xv))
forward:
F_dcsn: D_dcsn -> D_cntn @via |
xv_tilde ~ D_dcsn
a_tilde = astar(xv_tilde)
xe_tilde = g_ve(xv_tilde, a_tilde)
Now methods are attached via methodization, externally to the stage file:
- Methodization attaches methods/schemes to named operator instances (e.g., E_w, argmax_a) and to movers.
- Movers will typically require an APPROX scheme, since they return an approximate function representation under ρ.
- If there is a draw in the mover (e.g., w ~ D_w), methodization must attach a sampling scheme for that draw operator.
Clarification (push-forward + kernels): forward movers are best understood as push-forward operators derived from (i) a declared transition kernel (e.g., g_av) and (ii) declared shock distributions (e.g., w ~ D_w). The kernel itself is a pure function object and does not need a method attachment. Methodization attaches schemes/methods to the operator instances that implement the push-forward (draw ~, push-forward / simulation / quadrature), not to the kernel definition.
Corollary (when kernels “need methods”): a kernel only becomes “method-relevant” when you intend to evaluate it under ρ and its evaluation invokes non-primitive operator instances (e.g., E_w, argmax, APPROX/interpolation, draws). In that case, methods still attach to those operators—not to “the equation” as such.
Level 4: Movers (Functional Operators)¶
Movers (\(\mathbf{MOVERS}\)) are operators that transform functions or distributions:
symbols:
movers:
# Backward movers (value iteration)
"dcsn_to_arvl": B_arvl: [Xv -> R] -> [Xa -> R]
"cntn_to_dcsn": B_dcsn: [Xe -> R] -> [Xv -> R, Xv -> A]
# Forward movers (distribution evolution)
"arvl_to_dcsn": F_arvl: Dist(Xa) -> Dist(Xv)
"dcsn_to_cntn": F_dcsn: Dist(Xv) -> Dist(Xe)
Movers are the central objects that the methodization functor acts upon. Each mover: - Has a root space (domain of input function/distribution) - Has a tip space (domain of output function/distribution) - Contains references to registry operators (see §3.5)
Under Υ, movers map to mathematical operators on function spaces or distribution spaces.
3.4 Symbol Categories (Detailed)¶
This section unpacks the partition in §3.2 into concrete symbol categories and explains what role each category plays in the pipeline.
WIP note (important): the boundary between what must be explicitly declared in symbols: vs what can be derived from declarations (e.g., an induced shock distribution) is still evolving. For now, the safest rule is: if a name appears anywhere in equations/perches/movers, it must be declared in symbols: (or be a built-in primitive/operator).
Throughout this subsection, keep in mind the (intended) division of labor across blocks:
- symbols: is the stage’s typing environment (what names exist, their kinds, and their domains/codomains).
- operators: is the stage’s methodization surface (which operator templates/classes the methodization functor may attach schemes/methods to for this stage).
- equations: and perches: contain the symbolic bodies that use these names.
3.4.1 Spaces¶
Spaces are the basic domains on which all other objects live.
Syntax:
symbols:
spaces:
"arrival state space": Xa @def R+
"decision state space": Xv @def R+
"continuation state space": Xe @def R+
"shock space": W @def R
"action space": A @def Interval(a_min, a_max)
Role: - Provide domains/codomains for functions - Anchor the types of variables and shocks
Each \(X \in \mathbf{SPACES}\) is a type-level object; no numeric values are attached at this stage.
Important (WIP but core): any free names appearing inside a space constructor (e.g., a_min, a_max inside Interval(a_min, a_max)) must themselves be declared somewhere in symbols: (typically as parameters or settings).
3.4.2 Variables (States, Controls, Shocks)¶
Variables are binder names used in signatures and equation bodies. They become arguments of functions and movers under ρ (host-language parameters). In the Backus framing they are not “primitive objects”; we still declare them to:
- make type judgments explicit (e.g., xa @in Xa)
- ensure compositions are well-formed (domains/codomains match)
- support deterministic operator-instance identification (e.g., recognizing E_w as expectation over the shock variable w)
Syntax:
symbols:
variables:
states:
"arrival wealth": xa @in Xa
"decision wealth": xv @in Xv
"continuation wealth": xe @in Xe
controls:
"consumption": a @in A
shocks:
"income shock": w @in W, w ~ Normal(μ_w, σ_w)
Subcategories:
- State variables (under states:): Describe the endogenous state of the system
- Control variables (under controls:): Decision variables/controls
- Shock variables (under shocks:): Exogenous random variables with specified distributions
Variables are syntactic binders: they appear as arguments in function declarations and in mover signatures.
3.4.3 Parameters¶
Parameters are symbols that are not variables: they are fixed for a given calibration but can vary between calibrations.
Syntax:
symbols:
parameters:
"discount factor": β @in (0,1)
"risk aversion": γ @in R+
"interest rate": r @in R+
"shock mean": μ_w @in R
"shock std dev": σ_w @in R+
Role: - Act as inputs to the parameterization functor - Typically numerical: discount factors, risk aversion, technology parameters, etc. - Can be attached at the mathematical layer (Υ) since they belong to the mathematical problem
Parameters live in \(\mathbf{PARAMS}\) and are later given values by the parameter calibration \(\mathbb{C}_{\text{param}}\).
Interpretation note: in the “everything is a function object” framing, a parameter symbol is effectively a nullary function object constrained to lie in a declared set (e.g., β @in (0,1)), which becomes a concrete numerical value only after calibration.
3.4.4 Settings¶
Settings are the "free knobs" of numerical methods: grid sizes, tolerances, iteration caps, quadrature nodes, etc.
Syntax:
symbols:
settings:
# Grid sizes
"arrival grid points": n_xa @in Z+
"decision grid points": n_xv @in Z+
"continuation grid points": n_xe @in Z+
# Grid boundaries
"arrival grid min": grid_min_xa @in R+
"arrival grid max": grid_max_xa @in R+
# Solver settings
"solver tolerance": tol @in R+
"max iterations": max_iter @in Z+
# Quadrature settings
"quadrature nodes": n_nodes @in Z+
Role: - Do not appear in the economic model primitives - Only appear as arguments of method schemas and (methodized) movers - Only meaningful after methodization (they parameterize numerical schemes under ρ)
Settings live in \(\mathbf{SETTINGS}\) and are given values by the settings calibration \(\mathbb{C}_{\text{settings}}\).
Key distinction from parameters: - Parameters belong to the mathematical problem and can be attached at the Υ level - Settings belong to the numerical implementation and are only meaningful at the ρ level after methodization
3.4.5 Functions¶
Functions define the structural building blocks and solution objects of the model.
Syntax:
symbols:
functions:
inputs: # Stage inputs (provided externally)
"continuation value": Ve: Xe -> R @input
primitives:
"arrival to decision transition": g_av: Xa × W -> Xv
"decision to continuation transition": g_ve: Xv × A -> Xe
"utility function": u: A -> R
"Q kernel": Q_kernel: Xv × A -> R
outputs: # Stage outputs (produced by movers)
"arrival value": Va: Xa -> R
"decision value": Vv: Xv -> R
"optimal policy": astar: Xv -> A
Subcategories: - Inputs: Functions provided from outside the stage - Primitives: Structural functions (transition kernels, utility) — the mathematical model itself - Outputs: Solution functions computed by movers (value functions, policies)
Consistency rule (WIP but intended): anything listed in stage.inputs / stage.outputs must be declared under symbols.functions and/or symbols.distributions, and the mover signatures in symbols.movers must be consistent with those I/O declarations.
Function evaluation¶
In T core (formerly “DDSL-CORE”), a symbol denotes a function iff it is (a) declared under symbols.functions, or (b) introduced by an explicit function definition (e.g. an @mapsto … @via block). Otherwise, the symbol denotes a field: an element of some space (possibly perch-indexed) whose value is determined by the surrounding equation context.
A new function can be defined using the CORE @mapsto form (preferred), e.g. f: [x, y] @mapsto z @via | .... (Alternative sugar such as f[x_, y_] := ... is equivalent.)
function-definition #core-vs-sym¶
Relationship to dolo-plus (DDSL-SYM): - Perch-default evaluation: most function-like symbols are interpreted as functions of the current perch’s state variables. Decision-perch objects are unmarked; arrival/continuation use
[<]/[>]. - Intermediate assignments: an equation likey = x + zintroduces a local temporary (field) in CORE; under Υ it corresponds to a derived expression \(y(x,z)\). Ifyis unambiguous, SYM may allow writingydirectly; otherwise spell out arguments (e.g.y[x,z]) or rename to avoid collisions with declared states/controls. - Example (perch disambiguation): ifmis the decision state,x_post = m + 2refers to decision-perchm. If you also define a continuation-perch quantitym[>](e.g. from an inverse-Euler step), it must be referenced explicitly asm[>]. Any “inverse” mapping back to a decision variable should be written as an explicit operator (placeholder:INVERT[m]), not inferred implicitly.
3.4.6 Distributions¶
Distributions represent probability measures over state spaces.
Syntax:
symbols:
distributions:
inputs:
"arrival distribution": D_arvl: Dist(Xa)
internal:
"shock distribution": D_w: Dist(W)
outputs:
"decision distribution": D_dcsn: Dist(Xv)
"continuation distribution": D_cntn: Dist(Xe)
Interpretation note (WIP):
- A shock declaration like w @in W, w ~ Normal(μ_w, σ_w) induces (under Υ) a mathematical distribution over W.
- We still typically name a corresponding distribution symbol (e.g., D_w) so that draws inside forward movers can be written explicitly (w ~ D_w).
3.4.7 Constraints¶
Constraints define state-dependent feasible sets.
Syntax:
Under Υ, \(\Gamma(x_{\sim})\) is a subset of the action space \(A\) representing feasible actions at state \(x_{\sim}\).
WIP: we will likely standardize a distinct arrow (e.g.,
->>) to denote set-valued correspondences. For now, treatGammaas set-valued even if some examples still use a single arrow.
3.4.8 Movers¶
Movers are the primary functional operators in the ADC/Bellman framework. They transform functions or distributions between perches.
Syntax:
symbols:
movers:
# Backward movers (value iteration)
"dcsn_to_arvl": B_arvl: [Xv -> R] -> [Xa -> R]
"cntn_to_dcsn": B_dcsn: [Xe -> R] -> [Xv -> R, Xv -> A]
# Forward movers (distribution evolution)
"arvl_to_dcsn": F_arvl: Dist(Xa) -> Dist(Xv)
"dcsn_to_cntn": F_dcsn: Dist(Xv) -> Dist(Xe)
Key points: - Each mover has a root space (input function/distribution domain) and tip space (output domain) - Movers compose registry operators (§3.5) to implement their transformations - Movers are the central objects that the methodization functor acts upon - The factored Bellman operator is expressed as composition of movers
Interpretation note: movers live at the functional level (operators that take functions/distributions as inputs and return functions/distributions). Under Υ they map to mathematical operators; under ρ they map to approximate computational operators after methodization + calibration.
3.4.9 Operator Declarations¶
The stage declares which operators are in scope for methodization in an operators: block. This enables the methodization functor to:
- validate that all operator usages in mover bodies are legal, and
- attach schemes/methods/settings profiles (externally) without relying on AST “addresses”.
Note: methods are not specified here — they come from a separate methodization config file.
Syntax:
operators:
# Logical operators (registry built-ins)
- name: E_{}
class: expectation
description: "Generic expectation operator"
- name: argmax_{}
class: optimization
description: "Generic argmax operator"
# Movers (stage-local operator instances that will be methodized)
- name: dcsn_to_arvl
class: mover
kind: backward
perch_from: decision
perch_to: arrival
- name: cntn_to_dcsn
class: mover
kind: backward
perch_from: continuation
perch_to: decision
- name: arvl_to_dcsn
class: mover
kind: forward
perch_from: arrival
perch_to: decision
- name: dcsn_to_cntn
class: mover
kind: forward
perch_from: decision
perch_to: continuation
# Approximation operator
- name: APPROX
class: approximation
Key point: This block says "these are the operators that appear in the stage" — no methods, no numbers. The methodization functor uses this to: 1. Match operator templates/instances against the operator registry (identity + signatures) 2. Look up schemes/methods in the operation registry using the model-specific methodization config 3. Produce methodized operators (scheme + method chosen, settings still symbolic)
3.4.10 Perches¶
Perches are syntactic labels for local modeling contexts that define the ADC structure.
Critical interpretation: a perch is an information set. More precisely, each perch corresponds to a \(\sigma\)-algebra in a filtration describing what is known at that point within a stage:
The associated "perch state space" (e.g. \(X_{\prec}, X_{\sim}, X_{\succ}\)) should be read as a space of information states (sufficient statistics) rather than as a claim about a latent physical state. This is what lets us treat common economic models as Markov while still aligning with the more general POMDP framing.
Perch tags in SYM/dolo-plus are therefore measurability claims (adaptedness): writing \(z\) (unmarked, i.e. at the decision perch) asserts that \(z\) is measurable with respect to \(\mathcal{F}_{\sim}\), i.e. a function of decision-time information.
This is why “perches are endpoints of operators” is the wrong mental model: expectation/maximization/etc. are operators inside mover bodies; perches exist to record what is observable/conditionable at each step.
Syntax (movers are purely symbolic — no @methods here):
perches:
sequence:
forward: [arrival, decision]
backward: [decision, arrival]
arrival:
backward:
B_arvl: Vv -> Va @via |
xv = g_av(xa, w)
Va(xa) = E_w[Vv(xv)]
forward:
F_arvl: D_arvl -> D_dcsn @via |
xa_tilde ~ D_arvl
w ~ D_w
xv_tilde = g_av(xa_tilde, w)
decision:
backward:
B_dcsn: Ve -> [Vv, astar] @via |
astar(xv) = argmax_{a in Gamma(xv)} Q_kernel(xv, a)
Vv(xv) = Q_kernel(xv, astar(xv))
forward:
F_dcsn: D_dcsn -> D_cntn @via |
xv_tilde ~ D_dcsn
a_tilde = astar(xv_tilde)
xe_tilde = g_ve(xv_tilde, a_tilde)
Key point: Movers in perches are purely symbolic. They describe what mathematical operations occur, not how to compute them numerically. Methods are attached externally via the methodization config file (see §4).
3.5 Operator Registry¶
The operator registry is the catalog of built-in operator templates that may occur inside mover bodies (expectation, optimization, draws, approximation, etc.). It exists so that: - parsing/validation can recognize operator usages in mover bodies, and - methodization can attach schemes/methods/settings to named operator instances deterministically.
Important distinction (WIP but crucial):
- The operator registry (this section) is about operator identity + signatures + instance formation.
- The operation registry (e.g., operation-registry.yml) is about available schemes/methods/options for each operator class.
3.5.1 Registry Structure¶
The registry categorizes built-in operators by type:
# Built-in operators (implicit in every DDSL model)
operator_registry:
expectation:
- E_{shock}: ExpectationOperator
# E_w, E_ε, etc. - expectation over named shock variable
domain: "function on (state × shock) space"
codomain: "function on state space"
optimization:
- argmax_{action}: ArgmaxOperator
# argmax_{a ∈ Γ(x)} f(x,a)
domain: "function on (state × action) space"
codomain: "function on state space (returns optimal action)"
- max_{action}: MaxOperator
domain: "function on (state × action) space"
codomain: "function on state space (returns optimal value)"
draw:
- ~: DrawOperator
# x ~ D means draw x from distribution D
domain: "distribution"
codomain: "sample (for simulation)"
pushforward:
- PushForward: PushForwardOperator
# often implicit in forward movers; included here as a semantic operator
domain: "distribution on domain space"
codomain: "distribution on codomain space"
approximation:
- APPROX: ApproximationOperator
# converts mathematical operator to approximate numerical operator
domain: "mathematical operator + scheme"
codomain: "approximate operator on discrete representations"
3.5.2 Expectation Operators¶
Expectation operators integrate over shock distributions:
| Operator | Signature | Mathematical Meaning |
|---|---|---|
E_w |
[X × W → R] → [X → R] |
\((\texttt{E}_w f)(x) = \mathbb{E}_w[f(x, w)]\) |
E_ε |
[X × E → R] → [X → R] |
\((\texttt{E}_\varepsilon f)(x) = \mathbb{E}_\varepsilon[f(x, \varepsilon)]\) |
Usage in movers (purely symbolic):
The subscript (e.g., w) identifies which shock is being integrated over. The methodization functor uses this to match against the methodization config and attach the appropriate quadrature scheme.
3.5.3 Optimization Operators¶
Optimization operators solve for optimal actions:
| Operator | Signature | Mathematical Meaning |
|---|---|---|
argmax_{a ∈ Γ(x)} |
[(X × A) → R] → [X → A] |
\((\texttt{argmax} f)(x) = \arg\max_{a \in \Gamma(x)} f(x, a)\) |
max_{a ∈ Γ(x)} |
[(X × A) → R] → [X → R] |
\((\texttt{max} f)(x) = \max_{a \in \Gamma(x)} f(x, a)\) |
Usage in movers (purely symbolic):
B_dcsn: Ve -> [Vv, astar] @via |
astar(xv) = argmax_{a in Gamma(xv)} Q_kernel(xv, a)
Vv(xv) = Q_kernel(xv, astar(xv))
3.5.4 Sampling/Draw Operators¶
The tilde operator ~ represents sampling from a distribution:
| Operator | Signature | Mathematical Meaning |
|---|---|---|
x ~ D |
Dist(X) → X |
Draw \(x\) from distribution \(D\) |
Usage in movers (purely symbolic):
Each shock variable can only be drawn once per mover. The methodization functor interprets w ~ D_w as a "draw + pushforward" operation and attaches the appropriate sampling method from the methodization config.
3.5.5 The APPROX Operator¶
The APPROX operator is fundamental to the methodization process. It transforms mathematical operators into approximate numerical operators:
Key properties: - Takes a mathematical operator and a chosen numerical scheme - Yields an approximate operator acting on discrete primitive objects - The first step of methodization is attaching APPROX to each mover - The approximate operator under ρ is not equal to the original under Υ; equality holds only in the limit
Conceptual diagram:
math object ← Υ ← syntactic mover → M → {APPROX(mover), methods} → ρ → AST → approximate math
↑
(equals true math only in limit)
3.5.6 Operator Disambiguation¶
For the methodization functor to work, each operator instance in a stage must be uniquely identifiable without referring to an AST “address”:
- Expectation operators: Disambiguated by shock subscript (
E_w,E_ε) - Optimization operators: Disambiguated by action variable and (typically) the constraint/correspondence name
- Draw operators: Disambiguated by the variable being drawn and its distribution
- Multiple methods for “the same math operator” (WIP): if you need two different methods for what looks like the same operator (e.g., two expectations over
w), you should introduce two distinct operator instances in the stage (e.g., via an explicit operator table/naming convention) rather than attaching methods to occurrence addresses.
The registry ensures:
- Each syntactic usage of E_w, argmax, ~, etc. resolves to a unique registry entry
- Methodization uses the registry to attach schemes to each operator instance
- No ambiguity about which operator is being transformed
3.6 Grammar and Function Objects¶
Building on the symbol categories, we can now define function objects more precisely.
Let \(\mathbf{SYM}\) be the set of all symbols appearing in a model. A function object is:
A function object \(f \in \mathbb{F}\) is a finite sequence of symbols from \(\mathbf{SYM}\) that satisfies the DDSL grammar (e.g., looks like a well-formed declaration such as
g_av: [xa, w] @mapsto xv @via | xv = xa * r + w).
Function Object Patterns¶
Function objects follow specific patterns based on their category:
- Primitive functions: name: domain -> codomain
- Solution functions: name: domain -> codomain (often marked as @input in the stage, and often wrapped by APPROX after methodization)
- Movers: name: [domain -> range] -> [domain' -> range']
Composition and Evaluation¶
Function objects can be composed using standard functional notation:
- f(g(x)) represents composition
- E_w[f(x, w)] represents expectation
- argmax_{a ∈ Γ(x)} Q(x, a) represents optimization
3.7 Typing Environment¶
The typing environment is the internal object built from the symbols: block that supports validation and type checking.
Every symbol carries primitive set-theoretic operations or decorations that convey its mathematical meaning:
@deffor defining spaces (set definition)@infor set membership@mapstofor function definition@viafor equation body@inputfor stage inputs
Note: Methods are not annotated in the stage syntax. They are specified externally via the methodization config file (see §4).
Formally, the typing environment is a map:
assigning to each declared name a syntactic type object drawn from the type universe.
At a minimum, validation/type checking should enforce (WIP list):
- every referenced symbol is declared in symbols:
- mover signatures are consistent with declared function/distribution types
- operator instances used in mover bodies are recognizable by the operator registry (and can be matched to methodization config)
3.8 Calibration: Parameters vs. Settings¶
Attaching numbers is done outside the syntactic stage via a calibration functor. This is split into two parts:
Parameterization¶
maps economic parameters (β, γ, etc.) into primitive numeric objects.
Important: Parameters can be attached at the Υ (mathematical) level since they define the mathematical problem itself.
Settings¶
maps numerical settings (grid sizes, tolerances, etc.) into primitive numeric objects.
Important: Settings only have meaning at the ρ (computational) level after methodization has chosen specific numerical schemes.
WIP note: in the current three-file architecture, settings are often supplied via settings_profile labels introduced by methodization (i.e., calibration binds numerical values to the settings-profile records that parameterize chosen schemes), not only by directly mapping each raw setting symbol name.
Together:
3.9 Glossary¶
| Term | Definition |
|---|---|
| Symbol | A declared identifier with a type, kind, and mathematical meaning |
| Mover | A functional operator that transforms functions or distributions between perches |
| Operator | A built-in transformation (E, argmax, ~, APPROX) registered in the operator registry |
| Operator instance | A stage-local named instance of an operator template (e.g., E_w, argmax_a) that methodization can target |
| Operator registry | Catalog of built-in operator templates, signatures, and instance-formation rules (used for parsing/validation/methodization) |
| Operation registry | Catalog of numerical schemes/methods/options available for each operator class (e.g., operation-registry.yml) |
| Scheme | A numerical algorithm family (e.g., !gauss-hermite, !golden-section) |
| Method | A concrete instantiation of a scheme with settings |
| settings_profile | A label used by methodization to name a bundle of numerical settings that calibration must later supply |
| APPROX | The approximation operator that converts mathematical operators to numerical operators |
| Υ (Upsilon) | The mathematical meaning map: syntax → mathematical objects |
| ρ (rho) | The computational representation map: calibrated + methodized syntax → numerical/AST |
| Perch | A local modeling context in the ADC framework (arrival, decision, continuation) |
| Parameter | An economic constant (β, γ) that defines the mathematical problem |
| Setting | A numerical constant (grid size, tolerance) that parameterizes computational methods |
3.10 Summary¶
The symbol system provides:
- Complete declaration: Every symbol must be declared with a type
- Rich categorization: Symbol categories with specific roles (spaces, functions, distributions, movers, operators)
- Operator registry: Built-in operators with unique identification for methodization
- Type safety: Static typing through the typing environment
- Clean separation:
- Parameters (mathematical) vs. Settings (numerical)
- Syntactic declarations vs. calibration values
- Modularity: Perches organize symbols into coherent subcollections
- Methodization support: Operator disambiguation enables the methodization functor
This comprehensive symbol system ensures that DDSL models are: - Mathematically precise: All entities have clear types and semantics under Υ - Computationally tractable: Types and operators guide code generation under ρ - Easily validated: Type checking and operator registry catch errors early - Highly modular: Symbols can be reused and composed