35Software Engineering Patterns for Causal-Dynamical Modelling
Status: Draft
v0.4
35.1 Introduction
Part IV needs a software practice companion. This chapter describes how the bookβs Julia stack is organised, how a thin application connects identification to estimation, and how tests and dependency pins keep work reproducible. It is not a substitute for the conceptual chapters or the package manuals. The principles transfer to other languages; the concrete layout uses Julia. Together with reporting standards, it completes identify β estimate β display after the CDM synthesis.
35.2 Working stack
In these pages the working stack is four packages:
Exported names for identification and estimation match the literature (identify, do_intervention, run_lmtp_grid, run_mediation, dagplot, β¦). Temporal identity may use the closed lexicon in Table 8 (occasion, enduring, constitution, constitutive, influence, onset, replacement, deployment). The philosophical glossary belongs in this book (Chapter 9; glossary).
Non-mediated LMTP / MTP grids, sequential and survival LMTP, factor recodes, positivity, sensitivity, choose_policy, transport weights
Mediated EIF / NDEβNIE
Estimation β mediation (CausalMediation)
Mediation grids, moc, RI / natural / RT when needed
Cohort loaders; non-mediated LMTP
Display (DAGMakie)
Adjustment / SWIG / temporal layouts
Estimators
Application repos
Data, registries, concordance, domain labels for transport
Re-implementing EIF estimators
On General, pin CausalDynamics β₯ 0.4 and CausalTargeted β₯ 0.3.10 (General#165016; 0.3.7β0.3.9 skipped). CausalMediation 0.1.1 (factor-A interventional mediation) registers next from the package tip once AutoMerge accepts it. The book develops the packages/ checkouts. Soft faΓ§ades on CausalTargeted still forward to CausalMediation when that package is loaded.
When extending the book or the packages, keep Pearl/SciML identification and estimation names stable, allow the closed temporal lexicon on existing types, keep Whitehead glossary in book prose, and update Table 8 if you introduce new central mappings. For manuals and changelogs, use the repositories, not this chapter as API documentation.
Start from recommend_run_options(n; engine, n_mediators) (lean Super Learner when \(n < 80\); parallel=false by default).
Pass mixed-type baselines as-is (String / Bool / categorical are encoded with a fold-stable CovariateSchema in LMTP, sequential, survival, and mediation grids).
Inspect positivity / support (positivity_report or grid positivity=true) (Petersen et al. 2012).
For mediation, sweep nested MC (mediation_n_mc_sweep) until signs and SEs stabilise (Liu et al. 2024).
Report tipping-point / partial-\(R^2\) sensitivity (sensitivity_report) (Cinelli and Hazlett 2020).
Treat discovery graphs as sensitivity only (merge_discovery_sensitivity!), never as silent DAG replacement.
Continuous ODEs compose via optional SciML helpers (using OrdinaryDiffEq β solve_cdm), not a solver hard-dependency in core. Deferred continuous-time / filter modules are listed on the package Scope page.
CausalTargeted.jl is the LMTP estimation companion (not nested inside CausalDynamics): cross-fitted total-effect grids, sequential / survival LMTP, factor recodes (DiscreteTreatmentPolicy / run_discrete_lmtp and sequential policies), positivity, and run_gcomp with a refitting bootstrap (or n_boot=0 for an influence-function SE). Incomplete outcomes and covariates use Observable policies: handle_missing (:drop, :ipcw, :impute, :ipcw_impute) on LMTP, g-comp, sequential, and survival paths, with run metadata for strategy and PCH rung. Structural MAR/MNAR claims attach via CausalDynamics MissingnessSpec / identify(...; missingness=); opt-in posterior draws use impute_posterior and run_lmtp_grid(...; imputation=). Survival censoring IPCW is not the same object as MAR missing terminal \(S_T\). Book overview: Observational Methods. Package methods (causalβstatistical voice): CausalDynamics Missingness, CausalTargeted Missingness.
Dual-stack synthetic recovery vs R lives in the book repo under scripts/synthetic_benchmark/ (treat single-draw wins as provisional). Broader functionality, edge-case, and performance stress (mixed-type baselines, panels, small public tables) lives under scripts/stress_harness/ with the dataset catalog in data/catalog.toml. Methods write-up and dataset notebook: CausalTargeted stress validation (STRESS.md).
Layout principles: separate graphs, identification, static SCM, and discrete CDMs; keep estimation out of CausalDynamics; split LMTP (CausalTargeted) from mediation (CausalMediation); prefer weakdep extensions over hard dependencies for optional backends.
35.4 End-to-end: identify β LMTP
A thin application should identify on a DAG, then estimate with CausalTargeted (or CausalMediation for mediated contrasts; Ch. 20), without baking nuisance fitting into CausalDynamics. For discrete-time CDMs, prefer simulate_panel β sequential_spec_from_identification β execute_estimand (Ch. 28), with policies for factor \(A_t\); for event-free probability under MTP use SurvivalPolicy / run_survival_lmtp (same chapter; Ch. 23). The point-treatment path below remains the tabular default:
Certificate: backdoor adjustment = [:W]
LMTP TE at Ξ΄ = 0.5: 0.343 (mixed baseline encoded internally)
Static SCMs use the same stackβs GraphSCM / simulate_scm / do_intervention path (Chapter 4); discrete-time shared-\(U\) trajectories use DiscreteTimeCDM (Chapter 28). Book examples that need the host-immunity spine should load scripts/biological_showcases.jl (host_immunity_cdm, simulate_host_immunity_scenario, host_immunity_teaser_dag) rather than re-implementing the assignments inline.
35.5 Types and tests that matter here
The supported discrete-time type is approximately:
Chapter 28 builds models with this type and calls simulate / counterfactual. Prefer package tests as the template: unit tests for a single mechanism, integration tests that intervene and replay shared noise, and synthetic recovery checks with known truth in CausalTargeted / CausalMediation / the book benchmark scripts. Do not invent parallel APIs in application code that duplicate DoIntervention, DoSequence, LMTP grids, or mediation runners already exported by the packages.
35.6 Reproducibility
Pin Julia and major dependencies in Project.toml / Manifest.toml (this book targets Julia 1.13). On General: CausalDynamics 0.4+, CausalTargeted 0.3.7+ (fold-stable mixed-type baselines), CausalMediation 0.1.0+, DAGMakie (figures). Path Pkg.develop remains fine for book work before a release lands. Examples load dependencies via scripts/ensure_packages.jl.
35.7 Summary
The working stack separates identify (CausalDynamics), estimate LMTP (CausalTargeted) or mediation (CausalMediation), and display (DAGMakie for graphs; plot_mtp_curve for Ξ΄-indexed effect curves). Application repositories should stay thin: data and concordance, not reimplemented estimators. Layout, weakdep extensions, and shared-\(U\) tests are the practices that keep that separation honest.
the generative head is (or can be reduced to) a conjugate / factorised model after residualisation (e.g. CausalDynamics infer_backdoor_effect on demeaned \((y,x)\))
\(N\) is large and you need a fast posterior summary for a scalar effect \(\tau\) or a low-dimensional latent
you already sit on an AgeSCM-style pipeline: encoder β αΊ β GraphPPL head
Prefer Turing (NUTS or other MCMC) when:
the likelihood is non-conjugate, hierarchical, or otherwise awkward for the current GraphPPL factors (abduction soft likelihoods, partial pooling, experimental-design comparisons)
you need a joint posterior over several parameters, or a small-\(n\) check against a VI mean
you want posterior predictive replicates drawn from full MCMC (Ch. 21)
Also in the stack (not a PPL choice): for latent trajectories, prefer filters / smoothers / StateSpaceDynamics Laplace before either PPL; use Turing only on short series or for parameter blocks once the path is handled.
Rule of thumb: conjugate tabular ATE and belief updates β RxInfer; flexible or pedagogical continuous models β Turing; long dynamical latent paths β SSM tooling first. Local timings live in scripts/bayesian_benchmarks.jl.
Computational troubleshooting: if VI and NUTS disagree on a scalar \(\tau\), or NUTS shows unstable geometry on a small check, treat that as a fitting problem to diagnose (approximation class, scaling, multimodality) before expanding scientific claims (Gelman et al. 2026). Prefer recovering a known contrast from simulated CDM/SCM data first (Ch. 5, 21).
Design rule:αΊ is the PPL observation interface; TMLE-style propensity supports transport-weighted training. Resolve Graphs β€1.13 with RxInfer 5.x (DataStructures 0.18) in AgeSCM Project.toml, see AgeSCM docs/RXINFER_DEPS.md.
35.9.1 Representation bridge in the AgeSCM pattern
AgeSCM trains a spectral encoder and treats αΊ as the observation interface. In the owned stack the same contract is RepresentationSpec / encode_to_panel: the CNN (or linear map) is the encoder callable; the code columns are what identification, mediation, and generative L3 see. The chunk below uses a matched filter for speed; swap in a Lux or Flux encode without changing the DAG.
include(joinpath(dirname(Base.active_project()), "scripts", "book_bootstrap.jl"))@auto_using CausalDynamics DataFrames RandomStatisticsrng =Random.Xoshiro(42)n, p =80, 16grid =range(0, 1; length = p)bump =exp.(-((collect(grid) .-0.45) ./0.1) .^2)A =Float64.(rand(rng, n) .<0.5)Mstar =1.1.* A .+0.3.*randn(rng, n)S =randn(rng, n, p) .*0.1for i in1:n S[i, :] .+= Mstar[i] .* bumpendwide0 =DataFrame(A = A, Y =1.6.* Mstar .+0.2.*randn(rng, n))spec =RepresentationSpec(:spectrum, [:z1], Sm ->hcat(Sm * bump); role =:definitional)wide =encode_to_panel(wide0, S, spec)println(representation_certificate(spec))println("cor(z1, M*) β ", round(cor(wide.z1, Mstar); digits =3),"; columns for estimation: ", names(wide))
Mediation and LMTP then run on wide with :z1 as mediator or baseline feature; see CausalMediation methods and CausalTargeted Deep SCM estimation stress. Graph-constrained f_i and additive L3 on those codes are Chapter 28βs Deep SCM chunk.
35.9.2 CDM and estimation heads as software
Package layout (above) is half the story. Generative CDMs, PPL heads, and LMTP pipelines behave like software under test (Gelman et al. 2026):
Oracle recovery: simulate from a known SCM/CDM, run identification + estimation, assert \(\hat\tau\) (or learned dynamics) matches truth within tolerance.
Golden contrasts: keep a small fixed synthetic table (as in the RxInfer wiring chunk below) in CI or stress notebooks so regressions surface early.
Out-of-domain splits: treat LOSO / temporal hold-outs as regression tests when transport is claimed (Ch. 21).
Owned-package stress notebooks under CausalTargeted (and CausalDynamics Deep SCM stress) already follow this pattern; book chapters show the scientific side of the same checks.
35.9.3 Live wiring: CausalDynamics β RxInfer
The book environment now ships GraphPPL and RxInfer. A confounded Gaussian table recovers \(\tau\approx 2\) in a fraction of a second:
AgeSCM replaces raw spectra by an encoder αΊ before this head; the identification and VI steps stay the same. Timings: scripts/bayesian_benchmarks.jl.
Cinelli, Carlos, and Chad Hazlett. 2020. βMaking Sense of Sensitivity: Extending Omitted Variable Bias.βJournal of the Royal Statistical Society: Series B (Statistical Methodology) 82 (1): 39β67. https://doi.org/10.1111/rssb.12348.
DΓaz, IvΓ‘n, Nicholas Williams, Katherine L. Hoffman, and Edward J. Schenck. 2023. βNonparametric Causal Effects Based on Longitudinal Modified Treatment Policies.βJournal of the American Statistical Association 118 (542): 846β57. https://doi.org/10.1080/01621459.2021.1955691.
Laan, Mark J. van der, and Sherri Rose. 2011. Targeted Learning: Causal Inference for Observational and Experimental Data. Springer Series in Statistics. Springer.
Liu, Richard, Nicholas T. Williams, Kara E. Rudolph, and IvΓ‘n DΓaz. 2024. General Targeted Machine Learning for Modern Causal Mediation Analysis. https://doi.org/10.48550/arXiv.2408.14620.
Petersen, Maya L., Kristin E. Porter, Susan Gruber, Yue Wang, and Mark J. van der Laan. 2012. βDiagnosing and Responding to Violations in the Positivity Assumption.βStatistical Methods in Medical Research 21 (1): 31β54. https://doi.org/10.1177/0962280210386207.