Julia interfaces for Delay Differential Analysis (DDA).
The package supports both the established run_DDA_AsciiEdf binary and a native
Julia implementation of the ST, CT, CD, DE, and SY flavors. The native engine
can run on the CPU or use CUDA for its batched regression work.
The package is distributed via GitHub:
using Pkg
Pkg.add(url="https://github.com/sdraeger/DelayDifferentialAnalysis.jl")Pass the binary explicitly when possible:
binary_path = "/opt/dda/bin/run_DDA_AsciiEdf"If binary_path is omitted, the package checks DDA_BINARY_PATH and common
locations such as ~/.local/bin, ~/bin, /usr/local/bin, and /opt/dda/bin.
On Windows, rename the binary with an .exe suffix and pass that path, for
example binary_path="C:\\path\\to\\run_DDA_AsciiEdf.exe".
Use run_DDA for direct access to the binary. Arguments are keyword-only.
using DelayDifferentialAnalysis
result = run_DDA(
file_path="recording.edf",
channels=[1, 2, 3, 4],
flavors=["ST", "CT"],
binary_path="/opt/dda/bin/run_DDA_AsciiEdf",
model=[1, 2, 10],
derivative_points=4,
order=3,
delays=[7, 10],
WL=2048,
WS=1024,
time_range=(0, 50_000),
)
println(size(result.T)) # first two integer columns from the native output
println(size(result.t)) # derived time axis
println(size(result.A)) # remaining coefficient/error columns
println(size(result["ST"])) # flavor-specific output matrix
println(size(flavor_matrix(result, "CT")))channels is optional. If omitted or set to nothing, no -CH_list is passed
and channel selection is left to the binary.
Use load_results=false when the binary should run without loading output files
into Julia:
run_DDA(
file_path="recording.ascii",
flavors=["ST"],
binary_path="/opt/dda/bin/run_DDA_AsciiEdf",
input_format=:ascii,
model=[1, 2, 10],
derivative_points=4,
order=3,
delays=[7, 10],
out_fn="dda-output/run1",
load_results=false,
)The typed helpers run_st, run_ct, and run_de call the same execution path
with common flavor defaults and return typed results:
result = run_st(
file_path="recording.edf",
channels=[1, 2],
binary_path="/opt/dda/bin/run_DDA_AsciiEdf",
)
size(result.coefficients) # (n_channels, n_windows, n_coeffs)
result.errors # (n_channels, n_windows)
result.t # derived time axis
to_dataframe(result) # long-format table (requires DataFrames.jl)run_ct returns a CTResult (one row per channel pair), run_de a
DEResult (result.ergodicity). All three accept in-memory data via
data::AbstractMatrix (channels × samples) instead of file_path.
run_dda_matrix runs DDA directly on a samples × channels matrix without an
external binary. Channels and channel pairs use Julia's 1-based indices.
using DelayDifferentialAnalysis
result = run_dda_matrix(
samples;
device="cpu",
channels=[1, 2, 3],
flavors=["ST", "CT", "CD", "DE", "SY"],
window_length=200,
window_step=100,
delays=[7, 10],
model_terms=[1, 2, 10],
derivative_points=4,
order=4,
nr_tau=2,
)
st = result["ST"]
println(st.matrix)
println(st.row_labels)The CPU backend is the default. To use NVIDIA CUDA, install CUDA.jl in the
active environment and select a device with device="cuda" or
device="cuda:0":
using Pkg
Pkg.add("CUDA")
result = run_dda_matrix(samples; device="cuda:0", flavors=["ST", "CT"])CUDA.jl is optional and is loaded only for a CUDA device. Data preparation and flavor assembly remain on the CPU; CUDA accelerates the independent regression problems in batches.
Structure selection has three steps:
structure_selection_computecomputes and caches DDA outputs.structure_selection_readrestores a previous compute run after restarting Julia.structure_selection_selectreads cached outputs and chooses the lowest-error model/delay combination.
This split is useful on shared machines because expensive binary runs can be reused, inspected, and selected over different channel subsets without rerunning DDA.
make_MOD(N_MOD, DDAorder; nr_delays=2) creates Claudia-style binary model
matrices. Each row is one candidate model. Each column corresponds to one
monomial in the printed P_DDA table.
MOD = make_MOD(3, 3)
print_structure_selection(MOD)The terminal printer shows P_DDA, a compact checkmark table for MOD, and a
Unicode model equation for each row. The polynomial order is inferred from the
number of MOD columns. write_model_LaTeX remains available when LaTeX output
is needed explicitly.
run = structure_selection_compute(
file_path="recording.ascii",
channels=collect(1:11),
binary_path="/opt/dda/bin/run_DDA_AsciiEdf",
input_format=:ascii,
N_MOD=3,
DDAorder=3,
delays=(4 + 1):40,
derivative_points=4,
WL=3000,
WS=200,
prefix="RUN1",
num_cores=4,
)If MOD is omitted, N_MOD and DDAorder are used to call make_MOD. If
MOD_numbers is omitted, every row of MOD is computed. If channels is
omitted, no -CH_list is passed and the binary runs over its default channel
set.
prefix is a folder. It is created if needed. The compute step writes generated
tau files and DDA outputs there, for example:
RUN1/TAU_ALL__1_0
RUN1/TAU_ALL__2_1
RUN1/structure_selection_01_02_10_ST
RUN1/structure_selection_01_02_10.info
Output names encode the active MOD column indices as two-digit values joined
by underscores. Existing complete outputs are reused rather than overwritten.
Candidates are evaluated in randomized order by default; pass randomize=false
for deterministic order. num_cores controls how many independent DDA binaries
run concurrently and defaults to 1. Values above Sys.CPU_THREADS are capped
at the available logical CPU count.
The compute step writes RUN1/structure_selection.toml before starting the DDA
calls. Restore the same StructureSelectionRun in a later Julia session without
rerunning DDA:
run = structure_selection_read(prefix="RUN1")selection = structure_selection_select(run; channels=[1])
println(selection.best_model)
println(selection.best_delays)
println(selection.best_score)Use models to restrict selection to specific cached MOD row numbers:
selection = structure_selection_select(run; channels=[1], models=[1, 3, 7])structure_selection_select does not run the DDA binary. It only reads files in
run.prefix. If channels is omitted, all channels found in the cached output
are scored together. If a compute run used explicit channel IDs, selection maps
those same IDs back to their cached positions. channel=[...] is accepted as a
singular alias for channels=[...].
Pass model_scope=:per_channel to choose one model/delay combination per
channel:
per_channel = structure_selection_select(run; model_scope=:per_channel)The old one-shot structure_selection(...) wrapper is still available for
small interactive runs; prefer the compute/select split otherwise.
plot_structure_selection reads the same cached ST outputs without rerunning
the DDA binary. The :all view shows the winning model number
for every valid two-delay pair after aggregating over all windows:
plot_structure_selection(run; mode=:all, channels=[1], models=[1, 3, 7])The :time view selects the best model and delay pair independently at each
window and shows tau1 and tau2 against the raw DDA window coordinate T:
plot_structure_selection(run; mode=:time, channels=[1])Both views compare only models with two active delays. Symmetric models are
mirrored across the delay-pair diagonal; asymmetric models retain their delay
ordering. Colors identify row numbers in run.MOD.
Plotting functions require Plots.jl (Pkg.add("Plots")); it is loaded only
when a plot is requested.
plot_coefficients(result) # coefficient time series per channel
plot_heatmap(result; coeff_index=1) # channels × windows heatmap
plot_errors(result) # reconstruction error
plot_ergodicity(de_result) # DEResult ergodicity traceGroup-level helpers operate on vectors of typed results:
results = run_batch(files; variant="st") # many files, one flavor
group = collect_results(results) # GroupResult (ST/CT)
de_group = collect_results(de_results) # DEGroupResult
mean_over_windows(group)
permutation_test(group_a, group_b) # PermutationResult
compute_effect_size(group_a, group_b) # Cohen's d
compare_windows(result, 1:10, 11:20) # baseline vs test windows- Julia channel indices are 1-based.
input_formataccepts:ascii,:edf,"ascii", or"edf". If omitted,.edffiles emit-EDF; other extensions emit-ASCII.modelmaps to-MODEL. A vector is passed as model indices. A matrix is interpreted row-wise as monomial encodings and converted to model indices.derivative_pointsis the Julia name for the binary-dmparameter.WLandWSmap to-WLand-WS. They default tonothingand are not passed unless specified.WL_CTandWS_CTmap to-WL_CTand-WS_CT.tau_filemaps to-TAU_file. When provided, directdelaysare ignored for command generation and no-TAUlist is passed.tau2,model2,no_norm, andWN_listpass through to-TAU2,-MODEL2,-NoNorm, and-WN_listwhen specified.sampling_rate=nothingis the default and does not pass-SR. A scalar maps to-SR N; a tuple maps to-SR N1 N2.select=[...]passes a raw-SELECTmask and overridesflavors.out_fn=nothinguses a temporary output base. Passout_fnto keep output files at a specific path.
Parsed run_DDA results expose:
result.T: first two integer columns of the native output.result.t: derived time axis, computed fromT[:, 1],TM,derivative_points, andsampling_ratewhen available.result.A: remaining native output columns for the first returned flavor.flavor_matrix(result, "ST")(alsoresult["ST"]): direct flavor matrices when those outputs are present. Theresult.STproperty style is deprecated.
File-based calls infer labels from EDF headers or optional ASCII/TSV header
rows. Pass channel_labels to override labels explicitly.
mask = generate_select_mask(["ST", "SY"])
println(format_select_mask(mask)) # "1 0 0 0 0 1"
st = get_variant_by_abbrev("ST")
println(st.name)
path = find_binary("/opt/dda/bin/run_DDA_AsciiEdf")
println(path)| Abbreviation | Name |
|---|---|
ST |
Single Timeseries |
CT |
Cross-Timeseries |
CD |
Cross-Dynamical |
DE |
Dynamical Ergodicity |
SY |
Synchronization |
MIT