SURD (Synergistic-Unique-Redundant Decomposition)

repository·main·Indexed 43 days ago

https://github.com/ald-lab/surd

A Python-based causal inference framework designed for analyzing complex, chaotic systems. SURD decomposes causal interactions into synergistic, unique, and redundant components by measuring information increments between past and future events. It provides tools for quantifying information leak, computing joint Probability Mass Functions (PMF) via create_pfm, and performing parallel causal analysis using surd.run() and surd.run_parallel().

Tokens
8.5K
Snippets
28
Records
31
Agent score
54%

What's inside SURD

  1. What is SURD and how does it work?

    main

    SURD (Synergistic-Unique-Redundant Decomposition) is a causal inference method designed for complex and chaotic systems. It measures the increments of information gained about future events based on available information from past observations.

    Core Capabilities:

    • Causality Decomposition: Breaks down causal interactions into three distinct components: redundant, unique, and synergistic contributions.
    • Information Leak Quantification: Identifies the amount of causality that remains unaccounted for due to unobserved variables.
    • Non-intrusive Analysis: Requires only pairs of past and future events, making it suitable for both computational and experimental data.
    • Versatility: Can detect causal relationships in systems with multiple variables, varying time lags, and instantaneous links using a probabilistic framework.
  2. Install the mpart library for transport map estimation

    main

    If you need to use the transport map for estimating probability density functions, you must install the mpart library. The installation method depends on your operating system:

    • Linux/Standard Conda: Use conda install -c conda-forge mpart.
    • macOS (osx-arm64): Since mpart is not available on conda-forge for this architecture, install it via pip instead.
    # For standard conda environments
    conda install -c conda-forge mpart
    
    # For osx-arm64 users
    pip install mpart
  3. Install SURD via Conda

    main

    To set up the SURD environment, clone the repository and use conda to create an environment from the provided environment.yml file. This will install all required dependencies automatically.

    After creation, activate the environment using conda activate surd to begin using the library.

    conda env create -f environment.yml
    conda activate surd
  4. Calculate information leak

    main

    Information leak can be calculated by comparing the sum of redundant (Rd) and synergistic (Sy) components against the total entropy ($H$) of the target variable.

    Formula used in examples: info_leak = 1 - (sum(Rd.values()) + sum(Sy.values())) / H

    # Calculation of information leak using it_tools
    hist = it.myhistogram(Y[0,:].T, nbins)
    H  = it.entropy_nvars(hist, (0,) )
    info_leak = 1 - (sum(Rd.values()) + sum(Sy.values())) / H
  5. Configure analysis parameters for SURD

    main

    When setting up a causal analysis, define the following parameters to control the integration and discretization:

    • Nt: Total number of time steps for system integration.
    • samples: Number of samples to use for analysis (typically Nt minus a buffer to remove transient effects).
    • nbins: Number of bins for histogram discretization.

    Constraint: To ensure accurate probability density function (PDF) estimation, select nbins such that $N_{\rm{samples}} > {N_{\rm{bins}}}^d$, where $d$ is the dimension of the PDF.

    Nt = 5*10**7            # Total time steps
    samples = Nt-10000      # Samples after removing transients
    nbins = 50              # Histogram discretization bins
    nlag = 1                # Time lag for analysis
  6. Find the optimal time lag for causality analysis

    main

    When performing causality analysis on time-series data, the results are highly dependent on the time lag. A common strategy is to find the lag that maximizes the 'unique causality' between different variables (excluding self-causality).

    Workflow:

    1. Normalize your data (zero mean, unit variance).
    2. Iterate through a range of lag values.
    3. For each lag, compute the joint histogram of the variables.
    4. Run surd.surd(hist) to get the Redundant causality I_R.
    5. Sum the unique causalities for cross-variable interactions (e.g., key != (i + 1,)).
    6. Normalize this sum by the total entropy $H$ to find the relative strength.
    7. Select the lag that maximizes this value.
    # Simplified logic for finding optimal lag
    nlags_range = range(1, 3001, 1)
    cross_unique_lag = np.zeros((nvars, len(nlags_range)))
    
    for nlag in nlags_range:
        # Prepare lagged data
        Y = np.vstack([X[i, nlag:], X[:, :-nlag]])
        hist, _ = np.histogramdd(Y.T, bins=10)
        I_R, I_S, MI, info_leak = surd.surd(hist)
        H = it.entropy_nvars(hist, (0,))
    
        # Sum causalities for cross-variable interactions
        single_digit_keys = [key for key in I_R.keys() if len(key) == 1 and key != (i + 1,)]
        sum_causalities = sum(I_R[key] for key in single_digit_keys)
        
        cross_unique_lag[i, nlag - 1] = sum_causalities / H
    
    # The optimal lag is the index of the maximum value
    max_lag = cross_unique_lag[target_idx, :].argmax()
  7. Analyze different causal interaction patterns

    main

    SURD can be used to characterize several foundational building blocks of causal interactions:

    1. Mediator Variable: A system where one variable acts on another through a third variable (e.g., $Q_3 \rightarrow Q_2 \rightarrow Q_1$).
    2. Confounder Variable: A system where a single variable influences two others (e.g., $Q_3 \rightarrow Q_1$ and $Q_3 \rightarrow Q_2$).
    3. Synergistic Collider: A system where multiple variables work together to influence a single variable (e.g., $[Q_2, Q_3] \rightarrow Q_1$).
    4. Redundant Collider: A system where multiple variables provide the same information about their influence on another (e.g., $Q_2 \equiv Q_3 \rightarrow Q_1$).
  8. Find optimal time lag for causality analysis

    main

    To ensure accurate causality computation, you may need to find the optimal time lag for each variable. A common strategy is to select the lag that maximizes the unique causality (excluding self-causality).

    Algorithm:

    1. Iterate through a range of possible lags (nlags_range).
    2. For each lag, construct the lagged matrix $Y$ and compute the histogram.
    3. Run surd.surd(hist).
    4. Sum the redundant/unique contributions for all single-digit tuples (excluding the variable's own self-causality).
    5. Choose the lag that maximizes this sum.
    # Conceptual implementation for finding optimal lag
    for i in range(nvars):
        for nlag in nlags_range:
            # Prepare the data
            Y = np.vstack([X[i, nlag:], X[:, :-nlag]])
            hist, _ = np.histogramdd(Y.T, bins=nbins)
            I_R, I_S, MI, info_leak = surd.surd(hist)
            
            # Calculate sum of causalities for single-digit tuples (excluding self)
            single_digit_keys = [key for key in I_R.keys() if len(key) == 1 and key != (i+1,)]
            sum_causalities = sum(I_R[key] for key in single_digit_keys)
            
            # Track the sum to find the maximum
            unique_lag[i].append(sum_causalities)
  9. System requirements for SURD

    main

    SURD's computational demand increases with the complexity of the probability density functions being estimated. For optimal performance, the following specifications are recommended:

    • RAM: Minimum 16 GB
    • Processor: Quad-core processor with a minimum clock speed of 3.3 GHz per core.

    Performance benchmarks were established on macOS (ARM64, 16 GB RAM) and Linux (Red Hat 8.8-0.8).

  10. Simulate the Moran effect system

    main

    The Moran effect can be simulated using a custom function that models the interaction between growth rates, delays, and environmental noise.

    In the provided example, the system is defined by the following parameters:

    • N: Total number of time steps.
    • r1, r2: Growth rates for the $R$ variables.
    • s1, s2: Growth rates for the $N$ variables.
    • D1, D2: Time delays.
    • psi1, psi2: Sensitivity parameters to environmental noise.
    • R0: Initial value for $R$.
    • N0: Initial value for $N$.

    The function returns four arrays representing the time series for $R_1, N_1, R_2,$ and $N_2$.

    def moran_effect(N, r1, r2, s1, s2, D1, D2, psi1, psi2, R0, N0):
        # ... implementation ...
        return R1, N1, R2, N2
    
    # Usage
    N = 10**7
    r1, r2 = 3.4, 2.9
    s1, s2 = 0.4, 0.35
    D = 4
    psi1, psi2 = 0.5, 0.6
    R0, N0 = 1, 0.5
    
    R1, N1, R2, N2 = moran_effect(N, r1, r2, s1, s2, D1=D, D2=D, psi1=psi1, psi2=psi2, R0=R0, N0=N0)
  11. Run SURD causal analysis on time-series data

    main

    To perform SURD (Synergistic-Unique-Redundant Decomposition) analysis, you need to prepare your data as a 2D array where rows represent variables and columns represent time steps. You then pass this data to tmap.transport_map to compute the probability density function (PDF), and finally pass that PDF to surd.surd to extract causal metrics.

    Workflow:

    1. Prepare Data: Create a matrix Y where Y[i, :] is the time series for variable $i$. If using time-lagged analysis, ensure the dimensions of the target and predictor variables are aligned.
    2. Compute Transport Map: Use tmap.transport_map(Y, nbins) to get the pdf and grids.
    3. Decompose Causality: Use surd.surd(pdf) to calculate the decomposition components.

    Metrics returned by surd.surd(pdf):

    • I_R: Redundant information.
    • I_S: Synergistic information.
    • MI: Mutual Information.
    • info_leak: Information leak.
    import surd
    import transport_map as tmap
    import numpy as np
    
    # Y is your prepared data matrix (variables x time_steps)
    # nbins is the discretization level
    nbins = 50
    
    # 1. Compute the transport map PDF
    pdf, grids = tmap.transport_map(Y, nbins)
    
    # 2. Run SURD decomposition
    I_R, I_S, MI, info_leak = surd.surd(pdf)
    
    # 3. Print formatted results
    surd.nice_print(I_R, I_S, MI, info_leak)
  12. Calculate information leak for a signal

    main

    Information leak represents the portion of entropy not accounted for by redundant or synergistic causal contributions. To calculate it manually using the SURD outputs and it_tools:

    1. Generate a histogram of the target variable using it.myhistogram.
    2. Calculate the entropy of the target variable using it.entropy_nvars.
    3. Compute the leak as: 1 - (sum(Rd.values()) + sum(Sy.values())) / H.

    Where Rd and Sy are the dictionaries returned by surd.surd_hd() and H is the entropy.

    # Calculate information leak
    hist = it.myhistogram(Y[0,:].T, nbins)
    H  = it.entropy_nvars(hist, (0,) )
    info_leak = 1 - (sum(Rd.values()) + sum(Sy.values())) / H