DSL Parity With nirs4all Pipelines¶
Status: working contract.
Goal: the dag-ml DSL must be at least as expressive as the current nirs4all pipeline surface, while keeping dag-ml’s architecture: operators stay external, controllers live in bindings/hosts, and Rust compiles, validates, schedules and audits the DAG, campaign, OOF and leakage contracts.
This document tracks semantic parity first. The strict PipelineDslSpec is the
portable canonical form. A JSON compatibility importer now lowers serialized
nirs4all-style list/dict pipelines into that canonical form; Python object and
YAML frontends should be thin host-side serializers around the same importer.
Non-Negotiable Design Rules¶
Splits are campaign/controller invocations (
split_invocation), not graph operators.Any data/model shape mutation must be visible in public DSL fields:
shape,aggregation_policy,augmentation_policy,selection_policy,train_paramsandtuning.OOF safety is graph-visible through prediction edges with
requires_oofand fold alignment; refit/replay consumes validated caches/bundles.Branch, merge, concat, source fusion and stacking are DAG structure, not Python call-order side effects.
Runtime behavior belongs to controllers. The DSL records enough intent and contracts for Rust to reject unsafe execution and for bindings to route tasks.
Minimal aliases are the preferred frontend surface when an operator is unambiguous.
SNVshould compile as an opaque transform payload; the Python or native controller registry resolves it through manifestoperator_selectorsto the matching controller. Verbose forms are reserved for parameters, explicit ids, controller hints or ambiguous routing.Bindings should therefore expose registry aliases as first-class UX, not as a compatibility afterthought. A bare alias such as
SNVorStandardScalerremains the representation in the graph; aTransformerMixincontroller may build the concrete object and execute it after Rust has selected that controller by alias/class/function/ref/type selectors.When a controller registry is available during DSL compilation (
compile-pipeline-dsl --controllersorbuild-pipeline-dsl-plan), aliases that are not obvious by Rust heuristics may still be reclassified by matchingoperator_selectorsbefore ports are frozen. For example,ElasticSpectracan stay a bare string while a model controller declares that alias; if two operator kinds claim the same alias, compilation requires explicit syntax.
Parity Matrix¶
nirs4all surface |
dag-ml DSL representation |
Contract status |
|---|---|---|
plain preprocessing/transform step |
|
compiled to |
model step, named model, explicit params |
|
compiled to |
tuner / finetune controller |
|
compiled to external |
target/y processing |
|
compiled to |
splitters ( |
top-level |
deliberately outside graph nodes |
sequential grouping ( |
|
inlined during compilation while preserving child node contracts |
sample augmentation |
|
compiled as augmentation with |
feature augmentation |
|
compiled as augmentation with |
runtime data generation / synthetic samples |
|
compiled as |
feature selection / shape-changing processing |
|
shape plan validated and attached to campaign |
sample filters |
|
compiled to exclusion/filter nodes with explicit |
concat transform / multi-view feature fusion |
|
compiled to |
duplicated branches |
|
multiple branch predictions retained |
separation branches by source/metadata/tag/filter |
|
compiled to graph intent plus campaign |
multiple models per branch |
multiple |
compiled into distinct OOF inputs for downstream merge |
merge predictions/features/original data |
|
compiled to join nodes with OOF prediction edges and branch data edges; |
merge plus immediate meta-model |
|
compiled as model consuming OOF prediction inputs and optional original data |
stacking, multi-model top-level stacks |
repeated |
pending predictions are preserved until consumed |
per-branch/per-model selection ( |
|
selector targets and |
finetune / hyperparameter search metadata |
|
tuning intent is graph-visible on model/tuner nodes and generation dimensions; concrete search remains controller-side |
final train params |
|
preserved as |
|
|
compiled into deterministic |
structural |
|
expanded into explicit OOF-producing choices with namespaced node ids and generator metadata |
structural |
|
expanded into explicit Cartesian OOF-producing choices with namespaced node ids and fold-safe downstream merge inputs |
serialized list/dict nirs4all surface |
top-level |
compatibility importer lowers to canonical DSL; data-only branch feature merges and merge dicts are compiled, and data-only generator stages are fused with downstream model generators so OOF choices stay complete |
minimal aliases / plain operator refs |
short strings plus |
Rust infers only safe planning class: splitters become campaign split invocations, obvious estimators become model nodes, obvious tuners such as |
multiple nirs4all splitter declarations |
one campaign |
splitters remain outside graph nodes while preserving train/test + CV chains for host split controllers |
multisource data |
|
contract surface present; richer materialization belongs to dag-ml-data |
repetition/sample/group aggregation |
top-level/shape |
core runtime implemented for sample/target/group OOF |
tag/exclude filters |
|
compiled to graph nodes |
charts/reports |
|
compiled as a chart node; host controller decides side effects |
Current Gaps¶
Native JSON compatibility import exists for serialized nirs4all-style list/dict syntax, including minimal aliases and plain
class/functiondescriptors. Direct Python object/YAML parsing is still a binding-layer task: hosts must serialize live objects and splitters into portable descriptors before handing the DSL to Rust.The DSL node kinds compile and validate graph contracts. Smoke controllers now execute transform/model/tuner/data-generation/mixed-join paths, but production execution still needs host controller support for each operator family.
Separation branch materialization by source/metadata/tag/filter now has an explicit campaign
branch_view_planscontract. Production runtime-complete support still requires dag-ml-data providers and host controllers to consume those selectors when creating branch-local views.Merge selector scopes and basic selection contracts are compile-validated, and OOF edges are enforced; actual metric scoring and ranking remain the responsibility of selection and merge controllers.
Runtime generation is represented as external generator nodes/controllers for data-producing generators. The next open boundary is richer generated-model and provider-native generation backends; actual generator implementations should stay outside Rust unless they are pure DAG/campaign coordination primitives.
Required Regression Coverage¶
Compile every nirs4all canonical sample category into strict DSL equivalents: linear, feature augmentation, sample augmentation, branch, stacking/merge, concat transform, filters/splits, finetune/tuner, multisource and all-features.
For each shape-changing step, assert that
DataModelShapePlanexists and rejects unsafe augmentation/selection scopes.For every stacking/merge pattern, assert that upstream prediction edges carry
requires_oof=trueand fold alignment.For repeated/grouped data, assert that target/group OOF cache requirements survive bundle capture and replay.