Skip to main content

dda_rs/
types.rs

1use serde::{Deserialize, Serialize};
2
3/// Default DDA parameter values shared across wrappers.
4pub const DEFAULT_MODEL_TERMS: [i32; 3] = [1, 2, 10];
5pub const DEFAULT_MODEL_DIMENSION: u32 = 4;
6pub const DEFAULT_POLYNOMIAL_ORDER: u32 = 4;
7pub const DEFAULT_NUM_TAU: u32 = 2;
8pub const DEFAULT_WINDOW_LENGTH: u32 = 200;
9pub const DEFAULT_WINDOW_STEP: u32 = 100;
10pub const DEFAULT_DELAYS: [i32; 2] = [7, 10];
11
12/// Time range for analysis
13#[derive(Debug, Clone, Serialize, Deserialize)]
14pub struct TimeRange {
15    pub start: f64,
16    pub end: f64,
17}
18
19/// Preprocessing options
20/// Note: Preprocessing should be done before DDA analysis, not by this package
21#[derive(Debug, Clone, Serialize, Deserialize)]
22pub struct PreprocessingOptions {
23    pub highpass: Option<f64>,
24    pub lowpass: Option<f64>,
25}
26
27/// Algorithm variant selection
28#[derive(Debug, Clone, Serialize, Deserialize)]
29pub struct AlgorithmSelection {
30    pub enabled_variants: Vec<String>,
31    /// SELECT mask as 6-bit string (e.g., "1 0 1 0 0 0" for ST and CD)
32    /// Format: ST CT CD RESERVED DE SY
33    /// - ST: Single Timeseries (native output: _ST; compatibility: _DDA_ST)
34    /// - CT: Cross-Timeseries (native output: _CT; compatibility: _DDA_CT)
35    /// - CD: Cross-Dynamical (native outputs: _CD_DDA_ST and _CD_DDA_CT)
36    /// - RESERVED: Internal development function (not for user use)
37    /// - DE: Delay Embedding (output: _DE)
38    /// - SY: Synchronization (output: _SY)
39    #[serde(skip_serializing_if = "Option::is_none")]
40    pub select_mask: Option<String>,
41}
42
43/// Window parameters for DDA analysis
44#[derive(Debug, Clone, Serialize, Deserialize)]
45pub struct WindowParameters {
46    pub window_length: u32,
47    pub window_step: u32,
48    /// CT-specific window length (for Cross-Timeseries variant)
49    #[serde(skip_serializing_if = "Option::is_none")]
50    pub ct_window_length: Option<u32>,
51    /// CT-specific window step (for Cross-Timeseries variant)
52    #[serde(skip_serializing_if = "Option::is_none")]
53    pub ct_window_step: Option<u32>,
54}
55
56/// Delay parameters for DDA analysis
57/// These are the tau values passed directly to the -TAU CLI argument
58#[derive(Debug, Clone, Serialize, Deserialize)]
59pub struct DelayParameters {
60    /// List of delay values (tau) passed directly to the binary
61    /// Example: [1, 2, 3, 4, 5] will be passed as -TAU 1 2 3 4 5
62    pub delays: Vec<i32>,
63}
64
65/// MODEL parameters for DDA analysis (expert mode)
66#[derive(Debug, Clone, Serialize, Deserialize)]
67pub struct ModelParameters {
68    pub dm: u32,     // Embedding dimension (default: 4)
69    pub order: u32,  // Polynomial order (default: 4)
70    pub nr_tau: u32, // Number of tau values (default: 2)
71}
72
73/// Strategy for choosing the CCD conditioning set.
74#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
75#[serde(rename_all = "snake_case")]
76pub enum CcdConditioningStrategy {
77    /// Use every selected conditioning channel except the current target/source pair.
78    AllSelected,
79    /// Choose confounds from channels that best explain the target alone.
80    AutoTargetSparse,
81    /// Choose confounds from channels that look like shared parents of target and source.
82    AutoSharedParents,
83    /// Choose confounds with a block-OMP style greedy selector on the conditioned baseline fit.
84    AutoGroupOmp,
85}
86
87/// Per-variant channel configuration
88#[derive(Debug, Clone, Serialize, Deserialize)]
89pub struct VariantChannelConfig {
90    /// Selected channel indices for single-channel variants (ST, DE, SY)
91    #[serde(skip_serializing_if = "Option::is_none")]
92    pub selected_channels: Option<Vec<usize>>,
93    /// Channel pairs for CT variant
94    #[serde(skip_serializing_if = "Option::is_none")]
95    pub ct_channel_pairs: Option<Vec<[usize; 2]>>,
96    /// Directed channel pairs for CD variant
97    #[serde(skip_serializing_if = "Option::is_none")]
98    pub cd_channel_pairs: Option<Vec<[usize; 2]>>,
99    /// Optional conditioning set for CCD (Conditional Cross-Dynamical)
100    /// If omitted, the pure-Rust engine conditions on all other selected channels.
101    #[serde(skip_serializing_if = "Option::is_none")]
102    pub conditioning_channels: Option<Vec<usize>>,
103    /// Optional policy for choosing the CCD conditioning set.
104    #[serde(skip_serializing_if = "Option::is_none")]
105    pub conditioning_strategy: Option<CcdConditioningStrategy>,
106    /// Optional circular-shift surrogate offsets used by CCD significance variants.
107    #[serde(skip_serializing_if = "Option::is_none")]
108    pub surrogate_shifts: Option<Vec<usize>>,
109    /// Optional temporal smoothness strength for temporally regularized CCD variants.
110    #[serde(skip_serializing_if = "Option::is_none")]
111    pub temporal_lambda: Option<f64>,
112    /// Optional cap on the number of active sources for sparse multivariate CCD variants.
113    #[serde(skip_serializing_if = "Option::is_none")]
114    pub max_active_sources: Option<usize>,
115}
116
117/// Complete DDA request configuration
118#[derive(Debug, Clone, Serialize, Deserialize)]
119pub struct DDARequest {
120    pub file_path: String,
121    #[serde(alias = "channel_list")]
122    pub channels: Option<Vec<usize>>, // Channel indices (0-based)
123    pub time_range: TimeRange,
124    pub preprocessing_options: PreprocessingOptions,
125    pub algorithm_selection: AlgorithmSelection,
126    pub window_parameters: WindowParameters,
127    pub delay_parameters: DelayParameters,
128    /// Channel pairs for CT (Cross-Timeseries) analysis
129    /// Each pair is [channel_i, channel_j] where channels are 0-based indices
130    #[serde(skip_serializing_if = "Option::is_none")]
131    pub ct_channel_pairs: Option<Vec<[usize; 2]>>,
132    /// Channel pairs for CD (Cross-Dynamical) analysis
133    /// Each pair is [from_channel, to_channel] representing directed relationships
134    /// Format: [(1, 2), (1, 3), (1, 4)] → CH_list: 1 2 1 3 1 4
135    #[serde(skip_serializing_if = "Option::is_none")]
136    pub cd_channel_pairs: Option<Vec<[usize; 2]>>,
137    /// MODEL parameters (expert mode)
138    /// If not provided, defaults to dm=4, order=4, nr_tau=2
139    #[serde(skip_serializing_if = "Option::is_none")]
140    pub model_parameters: Option<ModelParameters>,
141    /// Model term indices passed to `-MODEL`.
142    /// If not provided, defaults to [1, 2, 10] for compatibility with dda-py/jl.
143    #[serde(
144        default,
145        skip_serializing_if = "Option::is_none",
146        alias = "model_params",
147        alias = "model_encoding",
148        alias = "model"
149    )]
150    pub model_terms: Option<Vec<i32>>,
151    /// Per-variant channel configurations (new format)
152    /// Maps variant IDs to their specific channel configurations
153    #[serde(skip_serializing_if = "Option::is_none")]
154    pub variant_configs: Option<std::collections::HashMap<String, VariantChannelConfig>>,
155    /// Input file sampling rate in Hz
156    /// When > 1000 Hz, the -SR argument will be added as [SR/2, SR]
157    #[serde(skip_serializing_if = "Option::is_none")]
158    pub sampling_rate: Option<f64>,
159}
160
161/// Variant-specific DDA result
162#[derive(Debug, Clone, Serialize, Deserialize)]
163pub struct VariantResult {
164    pub variant_id: String,      // "ST", "CT", "CD", "DE"
165    pub variant_name: String,    // "Single Timeseries (ST)", etc.
166    pub q_matrix: Vec<Vec<f64>>, // Q matrix for this variant [channels × timepoints]
167    #[serde(skip_serializing_if = "Option::is_none")]
168    pub channel_labels: Option<Vec<String>>, // Optional channel labels specific to this variant
169    #[serde(skip_serializing_if = "Option::is_none")]
170    pub error_values: Option<Vec<f64>>, // Error/rho values per window from DDA output
171}
172
173/// DDA analysis result
174#[derive(Debug, Clone, Serialize, Deserialize)]
175pub struct DDAResult {
176    pub id: String,
177    pub file_path: String,
178    pub channels: Vec<String>,
179    pub q_matrix: Vec<Vec<f64>>, // Primary variant Q matrix (for backward compatibility)
180    pub variant_results: Option<Vec<VariantResult>>, // All variant results
181    pub raw_output: Option<String>, // Optional: keep raw output for debugging
182    pub window_parameters: WindowParameters,
183    pub delay_parameters: DelayParameters,
184    pub created_at: String,
185    #[serde(skip_serializing_if = "Option::is_none")]
186    pub error_values: Option<Vec<f64>>, // Error/rho values per window from DDA output
187}
188
189impl DDAResult {
190    pub fn new(
191        id: String,
192        file_path: String,
193        channels: Vec<String>,
194        q_matrix: Vec<Vec<f64>>,
195        window_parameters: WindowParameters,
196        delay_parameters: DelayParameters,
197    ) -> Self {
198        Self {
199            id,
200            file_path,
201            channels,
202            q_matrix,
203            variant_results: None,
204            raw_output: None,
205            window_parameters,
206            delay_parameters,
207            created_at: chrono::Utc::now().to_rfc3339(),
208            error_values: None,
209        }
210    }
211
212    pub fn with_raw_output(mut self, raw_output: String) -> Self {
213        self.raw_output = Some(raw_output);
214        self
215    }
216
217    pub fn with_variant_results(mut self, variant_results: Vec<VariantResult>) -> Self {
218        self.variant_results = Some(variant_results);
219        self
220    }
221
222    pub fn with_error_values(mut self, error_values: Vec<f64>) -> Self {
223        self.error_values = Some(error_values);
224        self
225    }
226}