Modeling Language Implementation
The API described here is internal to Gen's design and is subject to changes with no deprecation.
Parsing @gen functions
Gen's built-in modeling languages are designed to preserve Julia's syntax as far as possible, apart from the Tilde syntax for calling generative functions, and the restrictions imposed on the Static Modeling Language. In order to preserve that syntax, including the use of non-Gen macros within @gen functions, we relegate as much of the parsing of @gen functions as possible to Julia's macro-expander and parser.
In particular, we adopt an implementation strategy that enforces a separation between the surface syntax associated with Gen-specific macros (i.e., @trace and @param) and their corresponding implementations, which differ across the Dynamic Modeling Language (DML) and the Static Modeling Language (SML). We do this by introducing the custom expressions Expr(:gentrace, call, addr) and Expr(:genparam, name, type), which serve as intermediate representations in the macro-expanded abstract syntax tree.
Each modeling language can then handle these custom expressions in their own manner, either by parsing them to nodes in the Static Computation Graphs (for the SML), or by substituting them with their implementations (for the DML). This effectively allows the SML and DML to have separate implementations of @trace and @param.
For clarity, below is a procedural description of how the @gen macro processes Julia function syntax:
macroexpandthe entire function body with respect to the calling module. This expands any (properly-scoped)@tracecalls toExpr(:gentrace, ...)expressions, and any (properly-scoped)@paramcalls toExpr(:genparam, ...)expressions, while also expanding non-Gen macros.- Desugar any tilde expressions
x ~ gen_fn(), including those that may have been generated by macros, toExpr(:gentrace, ...)expressions. - Pass the macro-expanded and de-sugared function body on to
make_static_gen_functionormake_dynamic_gen_functionaccordingly. - For static
@genfunctions, match:gentraceexpressions when adding address nodes to the static computation graph, and match:genparamexpressions when adding parameter nodes to the static computation graph. AStaticIRGenerativeFunctionis then compiled from the static computation graph. - For dynamic
@genfunctions, rewrite any:gentraceexpression with its implementationdynamic_trace_impl, and rewrite any:genparamexpression with its implementationdynamic_param_impl. The rewritten syntax tree is then evaluated as a standard Julia function, which serves as the implementation of the constructedDynamicDSLFunction.
Dynamic Modeling Language
The following methods are used to implement the semantics of the DML via non-standard interpretation:
Gen.make_dynamic_gen_function — Function
Construct a dynamic Gen function.
Gen.dynamic_param_impl — Function
Implementation of @param for the dynamic modeling language.
Gen.rewrite_dynamic_gen_exprs — Function
Rewrites :gentrace and :genparam with their dynamic implementations.
Gen.dynamic_trace_impl — Function
Implementation of @trace for the dynamic modeling language.
Gen.arg_to_ast — Function
Convert Argument structs to ASTs.
Gen.escape_default — Function
Escape argument defaults (if present).
Gen.choice_or_call_at — Function
Construct choice-at or call-at combinator depending on type.
Static Modeling Language
The following methods are used to parse functions written in the SML into static computation graphs:
Gen.StaticIRGenerativeFunction — Type
StaticIRGenerativeFunction{T,U} <: GenerativeFunction{T,U}Abstact type for a static IR generative function with return type T and trace type U.
Contains an intermediate representation based on a directed acyclic graph. Most generative function interface methods are generated from the intermediate representation.
Gen.make_static_gen_function — Function
Generates the code that builds the IR of a static Gen function.
Gen.gen_node_name — Function
Generate informative node name for a Julia expression.
Gen.parse_static_dsl_line! — Function
Parse line (i.e. top-level expression) of a static Gen function body.
Gen.parse_typed_var — Function
Parse optionally typed variable expressions.
Gen.parse_julia_expr! — Function
Parse a Julia expression and add a corresponding node to the IR.
Gen.parse_param_line! — Function
Parse @param line and add corresponding trainable param node.
Gen.parse_trace_expr! — Function
Parse @trace expression and add corresponding node to IR.
Gen.parse_and_rewrite_trace! — Function
Parse and rewrite expression if it matches an @trace call.
Gen.parse_assignment_line! — Function
Parse assignments and add corresponding nodes for the right-hand-side.
Gen.parse_return_line! — Function
Parse a return line and add corresponding return node.
Gen.parse_static_dsl_function_body! — Function
Parse static Gen function body line by line.
Gen.split_addr! — Function
Split nested addresses into list of keys.
Gen.resolve_symbols — Function
Look-up and return node names bound to each symbol in an expression.