Integration API

Functions for integrating CausalDynamics.jl with other packages (TMLE.jl, RxInfer/GraphPPL).

TMLE

CausalDynamics.prepare_for_tmleFunction
prepare_for_tmle(g, X, Y; node_names=nothing)

Prepare adjustment set from causal graph for use with TMLE.jl.

Arguments

  • g: Directed acyclic graph (DiGraph or CausalGraph)
  • X: Treatment node (integer or symbol)
  • Y: Outcome node (integer or symbol)
  • node_names: Optional mapping from node indices to symbols/names. Accepts Dict{Int,Symbol} or a Vector of names (index i names node i). If provided, returns symbol names; otherwise returns node indices. For CausalGraph, node names are automatically extracted from graph properties if not provided.

Returns

  • adj_set: Vector of confounders (symbols or integers) ready for TMLE.jl
  • is_identifiable: Boolean indicating if effect is identifiable via backdoor adjustment

Examples

using CausalDynamics, TMLE, DataFrames

# Create graph: Z → X → Y, Z → Y
g = DiGraph(3)
add_edge!(g, 1, 2)  # Z → X
add_edge!(g, 1, 3)  # Z → Y
add_edge!(g, 2, 3)  # X → Y

# With node names (Dict or vector indexed by node)
node_names = Dict(1 => :Z, 2 => :X, 3 => :Y)
confounders, identifiable = prepare_for_tmle(g, 2, 3; node_names=node_names)
# Returns: ([:Z], true)

confounders, identifiable = prepare_for_tmle(g, 2, 3; node_names=[:Z, :X, :Y])
# Returns: ([:Z], true)

# Without node names (returns indices)
confounders, identifiable = prepare_for_tmle(g, 2, 3)
# Returns: ([1], true)

# With CausalGraph (auto-detects node names)
g2 = CausalGraph(3)
add_edge!(g2, 1, 2)
add_edge!(g2, 1, 3)
add_edge!(g2, 2, 3)
set_node_prop!(g2, 1, :name, :Z)
set_node_prop!(g2, 2, :name, :X)
set_node_prop!(g2, 3, :name, :Y)
confounders, identifiable = prepare_for_tmle(g2, 2, 3)  # Auto-uses node names
# Returns: ([:Z], true)

See Also

  • backdoor_adjustment_set: Find backdoor adjustment set
  • is_backdoor_adjustable: Check if backdoor adjustment is possible
source
CausalDynamics.estimate_effectFunction
estimate_effect(g, data, X, Y; node_names=nothing, method=:tmle, kwargs...)
estimate_effect(g, X, Y; method=:tmle, kwargs...)

Estimate causal effect using identified adjustment set and TMLE.jl.

This function combines identification (CausalDynamics.jl) with estimation (TMLE.jl) in a single workflow. For CausalGraph, data and node names can be automatically extracted from graph properties.

Arguments

  • g: Directed acyclic graph (DiGraph or CausalGraph)
  • data: DataFrame or table with observed data (optional for CausalGraph if data is attached)
  • X: Treatment variable (integer index or symbol/name)
  • Y: Outcome variable (integer index or symbol/name)
  • node_names: Optional mapping from node indices to symbols/names. For CausalGraph, node names are automatically extracted from graph properties if not provided.
  • method: Estimation method (currently only :tmle supported)
  • kwargs...: Additional arguments passed to TMLE.jl estimation function

Returns

  • TMLE.jl result object with effect estimate, confidence intervals, etc.

Examples

using CausalDynamics, TMLE, DataFrames

# Create graph: Z → X → Y, Z → Y
g = DiGraph(3)
add_edge!(g, 1, 2)  # Z → X
add_edge!(g, 1, 3)  # Z → Y
add_edge!(g, 2, 3)  # X → Y

# Create data
data = DataFrame(
    Z = randn(100),
    X = rand([0, 1], 100),
    Y = randn(100)
)

# Estimate effect with node names
node_names = Dict(1 => :Z, 2 => :X, 3 => :Y)
result = estimate_effect(g, data, 2, 3; node_names=node_names)

# With CausalGraph - data and names attached
g2 = CausalGraph(3)
add_edge!(g2, 1, 2)
add_edge!(g2, 1, 3)
add_edge!(g2, 2, 3)
set_node_prop!(g2, 1, :name, :Z)
set_node_prop!(g2, 2, :name, :X)
set_node_prop!(g2, 3, :name, :Y)
attach_data!(g2, data)
result = estimate_effect(g2, 2, 3)  # Auto-uses attached data and names

Notes

  • Requires TMLE.jl to be loaded: using TMLE
  • If no adjustment set is found, a warning is issued but estimation may still proceed
  • Additional TMLE.jl arguments can be passed via kwargs...
  • For CausalGraph, if data is attached and node names are set, they are used automatically

See Also

  • prepare_for_tmle: Prepare adjustment set for TMLE.jl
  • backdoor_adjustment_set: Find backdoor adjustment set
  • attach_data!: Attach data to CausalGraph
source
CausalDynamics.get_tmle_confoundersFunction
get_tmle_confounders(g, X, Y; node_names=nothing)

Get confounders for TMLE.jl from causal graph.

Convenience function that returns just the confounders vector, without the identifiability check.

Arguments

  • g: Directed acyclic graph
  • X: Treatment node (integer or symbol)
  • Y: Outcome node (integer or symbol)
  • node_names: Optional mapping from node indices to symbols/names

Returns

  • Vector of confounders (symbols or integers) ready for TMLE.jl

Examples

using CausalDynamics

g = DiGraph(3)
add_edge!(g, 1, 2)
add_edge!(g, 1, 3)
add_edge!(g, 2, 3)

node_names = Dict(1 => :Z, 2 => :X, 3 => :Y)
confounders = get_tmle_confounders(g, 2, 3; node_names=node_names)
# Returns: [:Z]

See Also

  • prepare_for_tmle: Full preparation with identifiability check
  • backdoor_adjustment_set: Find backdoor adjustment set
source

RxInfer / GraphPPL (optional)

Requires using RxInfer to load the CausalDynamicsRxInfer extension.

CausalDynamics.prepare_for_rxinferFunction
prepare_for_rxinfer(g, X, Y; node_names=nothing, data=nothing)

Prepare causal graph and data for use with RxInfer.jl.

RxInfer.jl uses various data formats and requires explicit variable specifications. This function extracts the necessary information from a CausalGraph or prepares it from a plain graph.

Arguments

  • g: Directed acyclic graph (DiGraph or CausalGraph)
  • X::Int: Treatment node index
  • Y::Int: Outcome node index
  • node_names::Union{Dict{Int, Symbol}, Nothing}: Optional mapping from node indices to names. For CausalGraph, automatically extracted if not provided.
  • data: Optional data (DataFrame, NamedTuple, or other). For CausalGraph, automatically extracted if attached and not provided.

Returns

  • NamedTuple with fields:
    • graph: The underlying graph (DiGraph)
    • treatment: Treatment variable name (Symbol) or index (Int)
    • outcome: Outcome variable name (Symbol) or index (Int)
    • confounders: Vector of confounder names (Symbol) or indices (Int)
    • data: Data in format compatible with RxInfer
    • node_names: Dictionary mapping node indices to names
    • is_identifiable: Boolean indicating if effect is identifiable

Examples

using CausalDynamics, DataFrames

# With CausalGraph
g = CausalGraph(3)
add_edge!(g, 1, 2)
add_edge!(g, 1, 3)
add_edge!(g, 2, 3)
set_node_prop!(g, 1, :name, :Z)
set_node_prop!(g, 2, :name, :X)
set_node_prop!(g, 3, :name, :Y)
data = DataFrame(Z=randn(100), X=rand([0,1], 100), Y=randn(100))
attach_data!(g, data)

spec = prepare_for_rxinfer(g, 2, 3)
# Returns: (graph=..., treatment=:X, outcome=:Y, confounders=[:Z], data=..., ...)

# Use in RxInfer model
using RxInfer
# Use spec.treatment, spec.outcome, spec.confounders, spec.data

Notes

  • Data is returned as-is - RxInfer accepts various formats
  • Node names are required for meaningful variable names in models
  • If node names are not available, indices are used instead
  • RxInfer typically uses NamedTuples or dictionaries for data

See Also

  • prepare_for_turing: Prepare for Turing.jl
  • get_tmle_confounders: Get confounders for TMLE
source
CausalDynamics.infer_backdoor_effectFunction
infer_backdoor_effect(g, data, X, Y; kwargs...) -> BackdoorInferenceResult

Backdoor-adjusted variational inference for treatment effect τ (GraphPPL + RxInfer).

Requires using RxInfer before calling. See extension docstring for arguments.

source
CausalDynamics.ppl_data_from_specFunction
ppl_data_from_spec(spec; kwargs...) -> NamedTuple

Convert prepare_for_rxinfer output to RxInfer data (outcome, treatment, confounder matrix). Requires using RxInfer.

source
CausalDynamics.prepare_for_turingFunction
prepare_for_turing(g, X, Y; node_names=nothing, data=nothing)

Prepare causal graph and data for use with Turing.jl.

Turing.jl uses DataFrames or NamedTuples for data, and requires explicit variable names. This function extracts the necessary information from a CausalGraph or prepares it from a plain graph.

Arguments

  • g: Directed acyclic graph (DiGraph or CausalGraph)
  • X::Int: Treatment node index
  • Y::Int: Outcome node index
  • node_names::Union{Dict{Int, Symbol}, Nothing}: Optional mapping from node indices to names. For CausalGraph, automatically extracted if not provided.
  • data: Optional data (DataFrame, NamedTuple, or other). For CausalGraph, automatically extracted if attached and not provided.

Returns

  • NamedTuple with fields:
    • graph: The underlying graph (DiGraph)
    • treatment: Treatment variable name (Symbol) or index (Int)
    • outcome: Outcome variable name (Symbol) or index (Int)
    • confounders: Vector of confounder names (Symbol) or indices (Int)
    • data: Data in format compatible with Turing
    • node_names: Dictionary mapping node indices to names
    • is_identifiable: Boolean indicating if effect is identifiable

Examples

using CausalDynamics, DataFrames

# With CausalGraph
g = CausalGraph(3)
add_edge!(g, 1, 2)
add_edge!(g, 1, 3)
add_edge!(g, 2, 3)
set_node_prop!(g, 1, :name, :Z)
set_node_prop!(g, 2, :name, :X)
set_node_prop!(g, 3, :name, :Y)
data = DataFrame(Z=randn(100), X=rand([0,1], 100), Y=randn(100))
attach_data!(g, data)

spec = prepare_for_turing(g, 2, 3)
# Returns: (graph=..., treatment=:X, outcome=:Y, confounders=[:Z], data=..., ...)

# Use in Turing model
using Turing
@model function causal_model(spec)
    # Use spec.treatment, spec.outcome, spec.confounders, spec.data
    # ...
end

Notes

  • Data is returned as-is (DataFrame or NamedTuple) - Turing accepts both
  • Node names are required for meaningful variable names in models
  • If node names are not available, indices are used instead

See Also

  • prepare_for_rxinfer: Prepare for RxInfer.jl
  • get_tmle_confounders: Get confounders for TMLE
source
CausalDynamics.get_data_for_pplFunction
get_data_for_ppl(g; format=:dataframe)

Extract data from CausalGraph in a format suitable for probabilistic programming.

Arguments

  • g::CausalGraph: The causal graph with attached data
  • format::Symbol: Desired output format (:dataframe, :namedtuple, :dict)

Returns

  • Data in the requested format, or nothing if no data attached

Examples

using CausalDynamics, DataFrames

g = CausalGraph(3)
data = DataFrame(Z=randn(100), X=rand([0,1], 100), Y=randn(100))
attach_data!(g, data)

# Get as DataFrame (default)
df = get_data_for_ppl(g)  # DataFrame

# Get as NamedTuple (for Turing/RxInfer)
nt = get_data_for_ppl(g; format=:namedtuple)  # NamedTuple

# Get as Dict
dict = get_data_for_ppl(g; format=:dict)  # Dict

Notes

  • Requires data to be attached to the graph
  • Format conversion may have performance implications for large datasets
  • NamedTuple format is efficient for Turing.jl
  • Dict format is flexible for various PPL packages

See Also

  • get_data: Get data as-is
  • attach_data!: Attach data to graph
source

Associations.jl (optional)

Requires using Associations to load the CausalDynamicsAssociationsExt extension.

CausalDynamics.prepare_from_discoveryFunction
prepare_from_discovery(g, treatment, outcome; node_names=nothing, complete=false)

Run backdoor identification on a candidate graph from discovery (PC, OCE, domain knowledge).

The input graph is treated as structural hypothesis, not ground truth. Discovery typically returns a CPDAG or lag parent set under faithfulness and sufficiency assumptions.

Returns

  • confounders: adjustment set (symbols if node_names given, else node indices)
  • identifiable: whether a backdoor adjustment set exists

See Also

source
CausalDynamics.cpdag_to_dagFunction
cpdag_to_dag(g::Graphs.DiGraph) -> Graphs.DiGraph

Break remaining bidirectional edges in a PC output (CPDAG) to obtain a DAG.

When both i → j and j → i are present, drops j → i if i < j. Discovery outputs may still be only partially oriented; this is a pragmatic heuristic before calling backdoor identification.

source
CausalDynamics.oce_parents_to_temporal_specFunction
oce_parents_to_temporal_spec(parents, variables::AbstractVector{Symbol}) -> TemporalDAGSpec

Convert OCE OCESelectedParents output to a TemporalDAGSpec.

Arguments

  • parents: vector of parent selections (one per variable), e.g. from infer_graph(OCE(), ts)
  • variables: symbol name for each series index (variables[i] is variable i)

Lag convention

OCE stores embedding lags in parents_τs (typically negative, e.g. -1 for xⱼ(-1)). These map to LaggedEdge lags as lag = abs(τ), so τ = -1 becomes lag = 1 (parent at t-1 causes child at t), matching Ch. 28 unrolling.

source
CausalDynamics.digraph_with_namesFunction
digraph_with_names(g::AbstractGraph, names::AbstractVector{Symbol}) -> CausalGraph

Wrap g in a CausalGraph and attach :name properties from names.

length(names) must equal nv(g).

source

SciML / OrdinaryDiffEq (optional)

Requires using OrdinaryDiffEq to load CausalDynamicsSciMLExt for solve_cdm. The types and façade functions below are always exported from core.

CausalDynamics.AbstractCausalInterventionType
AbstractCausalIntervention

Root type for all do(·) interventions in CausalDynamics: static SCM assignments, discrete-time CDM sequences/policies, and continuous-time CDM interventions.

Dispatch on the concrete subtype and the model (SCM / DiscreteTimeCDM / ContinuousCDMSpec) determines how the intervention is applied.

source
CausalDynamics.ranked_variables_to_parentsFunction
ranked_variables_to_parents(ranking, variables; target, max_parents=2) -> Dict

Build a single-target parent map from a variable ranking (ranking[i] is the rank of variables[i], lower is better; omit target).

source
CausalDynamics.ode_problem_cdmFunction
ode_problem_cdm(spec, rhs!, u0, tspan, p; intervention=nothing, kwargs...)

Build an ODEProblem with length(u0) == length(spec.variables). Requires using OrdinaryDiffEq.

source
CausalDynamics.solve_cdmFunction
solve_cdm(spec, rhs!, u0, tspan, p; intervention=nothing, kwargs...) -> sol

Integrate a continuous CDM and return the SciML solution. Requires using OrdinaryDiffEq.

source
CausalDynamics.terminal_stateFunction
terminal_state(spec::ContinuousCDMSpec, sol) -> NamedTuple

Terminal endogenous state keyed by symbol. Requires using OrdinaryDiffEq.

source
CausalDynamics.state_seriesFunction
state_series(spec::ContinuousCDMSpec, sol) -> NamedTuple

Time series of each endogenous variable. Requires using OrdinaryDiffEq.

source
CausalDynamics.interventional_rhsFunction
interventional_rhs(rhs!, spec::ContinuousCDMSpec, intervention)

Wrap a continuous RHS for a continuous-CDM intervention. Requires using OrdinaryDiffEq.

source
CausalDynamics.DoPinType
DoPin

Hard pin: set the initial condition to value and hold the coordinate fixed (ẋᵏ := 0). SciML applies this without mutating u inside the RHS; a DiscreteCallback reasserts the value after accepted steps to control drift.

source
CausalDynamics.DoInitialConditionType
DoInitialCondition

Set only the initial value of variable to value (leave the RHS unchanged).

Corresponds to do(x₀ᵏ := ξ) in continuous-time CDMs [@peters2022causal].

source
CausalDynamics.forward_sensitivity_cdmFunction
forward_sensitivity_cdm(spec, rhs!, u0, tspan, p; kwargs...) -> sol

Forward local sensitivity of a continuous CDM via SciMLSensitivity. Requires using OrdinaryDiffEq, SciMLSensitivity.

source

Interventional Embedding Entropy (IEE)

Julia port of the smsxiaomayi/IEE reference algorithm (Shi et al.). Prefer mi = :auto (Associations KSG1 when loaded); use :reference for MATLAB concordance.

CausalDynamics.interventional_embedding_entropyFunction
interventional_embedding_entropy(x, y; p=1, k=2, theiler=nothing, n_delta=10, mi=:auto) -> Float64

Compute IEE from cause series x to effect series y (IntDC ranking score).

Arguments

  • x, y: univariate vectors, or d × T matrices (variables × time)
  • p: embedding order (delay length)
  • k: k-NN order for MI (k ≥ 2)
  • theiler: half-width of Theiler window (defaults to p)
  • n_delta: number of neighbours used as local perturbation proxies
  • mi: mutual-information estimator — :auto (Associations KSG1 when loaded, else reference), :associations (requires using Associations), or :reference (MATLAB-faithful MIknn port for concordance tests)
  • backend: deprecated alias for mi (kept for notebook sessions that still expect the old keyword)

References

  • Shi et al. (2026), The Innovation; arXiv:2407.01621
  • Reference code: https://github.com/smsxiaomayi/IEE
source

ODE parent discovery

Leave-one-environment ranking for continuous-CDM parent sets (CausalKinetiX reference method). Load DataInterpolations for cubic-spline derivatives.

CausalDynamics.infer_ode_parentsFunction
infer_ode_parents(times, trajectories, env, target; max_size=2, K=nothing)

Infer an ODEParentRanking for main-effect parent sets of target.

Uses cubic-spline derivatives when DataInterpolations is loaded; otherwise finite differences. Reference method: CausalKinetiX [@pfister2019causalkinetix].

source
CausalDynamics.score_ode_parent_setsFunction
score_ode_parent_sets(times, trajectories, env, target, models; differentiate=nothing)

Score each candidate parent-index set for target coordinate target.

Arguments

  • times: length-L observation grid
  • trajectories: vector of d × L matrices (one per repetition)
  • env: environment id per repetition
  • target: 1-based target coordinate
  • models: candidate parent-index sets
  • differentiate: (t, y) -> dy (defaults to finite_difference_derivative)
source