Skip to main content

SURD: Synergistic-Unique-Redundant Decomposition of causality

tests PyPI License: MIT Paper DOI

SURD is a method for causal inference in time series. It measures the information that past observations of a set of variables carry about the future of a target variable and splits it into redundant, unique and synergistic contributions. The method also quantifies the causality leak, the part of the future of the target that the observed variables cannot explain. SURD needs only pairs of past and future events, so it applies to simulations and experiments alike. It handles several variables, dependencies at different time lags and instantaneous links. The method is described in Martínez-Sánchez, Arranz & Lozano-Durán, Nature Communications 15, 9296 (2024). The subpackage surd.states resolves the same decomposition over the states of the variables, following Martínez-Sánchez & Lozano-Durán, Communications Physics 9, 15 (2025). See Causality by states.

SURD diagram

Installation

From PyPI:

pip install surd

From a clone of this repository:

git clone https://github.com/ALD-Lab/SURD.git
cd SURD
pip install .

With conda, the environment file installs the dependencies and the package:

conda env create -f environment.yml
conda activate surd

SURD depends on NumPy, SciPy, Matplotlib and tqdm and runs on Python 3.9 or newer, on Linux, macOS and Windows. The optional extras are:

  • pip install "surd[parallel]" adds pymp-pypi, which runs the loop over target variables of surd.run_parallel in parallel (Linux and macOS). Without it the targets run one after another with the same result. Example E03 of surd.states uses it too.
  • pip install "surd[transport]" adds mpart for density estimation with transport maps (example E07). On osx-arm64 use pip install mpart, since the conda package does not exist for that platform. See the MParT documentation.
  • pip install "surd[states]" adds scikit-image and scikit-learn for the isosurfaces of states.plot_states_3d and the density estimate of states.it_tools.hist_knn. The decomposition by states needs neither.
  • pip install "surd[examples]" adds JupyterLab and marimo to run the notebooks.
  • pip install "surd[docs]" adds Sphinx to build the documentation in docs/.

The cost of SURD grows with the number of variables and bins, since it estimates joint probability distributions. The examples run on a laptop with 16 GB of RAM.

Quick start

Two independent bits and a target equal to their XOR one step later. Each bit alone says nothing about the target, so the information is synergistic.

import numpy as np
import surd

rng = np.random.default_rng(0)
q1, q2 = rng.integers(0, 2, size=(2, 200_000))    # two independent bits in time
target = np.roll(q1 ^ q2, 1)                      # their XOR, one step later
Y = np.vstack([target[1:], q1[:-1], q2[:-1]])     # future of the target first, sources after
hist, _ = np.histogramdd(Y.T, bins=2)
I_R, I_S, MI, info_leak = surd.surd(hist)
surd.nice_print(I_R, I_S, MI, info_leak)
    Redundant (R):
        (1, 2)      : 0.0000
    Unique (U):
        (1,)        : 0.0000
        (2,)        : 0.0000
    Synergystic (S):
        (1, 2)      : 1.0000
    Information Leak:  0.00%

How to use the method

The input is a histogram of the joint distribution of the future of the target and the present of the sources. Axis 0 is the target and axes 1 to N are the sources. The outputs are dictionaries keyed by tuples of source indices, counted from 1.

Function What it does
surd.surd(p) Decomposition of the histogram p. Returns I_R (redundant terms, with the unique terms under the one-element keys), I_S (synergistic terms), MI (mutual information of each subset of sources with the target) and info_leak, the causality leak in [0, 1].
surd.surd_hd(Y, nbins, max_combs) Same decomposition from the signals Y (target first) for systems with many variables. Synergies are computed up to max_combs sources at a time. Returns I_R, I_S and MI.
surd.run(X, nvars, nlag, nbins, axs) Runs surd.surd with each of the nvars signals of X as target at lag nlag, prints and plots the result.
surd.run_parallel(...) Same as surd.run, with the targets in parallel when pymp is available.
surd.run_multiple_lags(X, nvars, nlag, nbins, max_combs, axs) Same as surd.run with all lags from 1 to nlag as sources.
surd.nice_print(I_R, I_S, MI, info_leak) Prints the terms normalized by the mutual information of the full set of sources and the leak in percent.
surd.plot(I_R, I_S, info_leak, axs, nvars, threshold) Bar chart of the normalized terms (blue redundant, red unique, orange synergistic) and a separate bar for the leak.

surd.it_tools has the entropies, conditional entropies, mutual information and transfer entropy used by the method. surd.analytic_eqs has the benchmark systems of the paper. surd.transport_map estimates densities with transport maps and needs mpart.

Scripts written for the previous layout, which added utils/ to sys.path and imported surd, it_tools, analytic_eqs and transport_map as top-level modules, keep working. The files in utils/ load the package.

Examples

The notebooks in examples/ reproduce the cases of the paper. Run them from the examples folder. They import the installed package and fall back to ../src from a plain clone.

Notebook What it shows
E01 Logic gates. A duplicated source is redundant, a copied source is unique, an XOR is synergistic.
E02 Building blocks of causal interactions: mediator, confounder, synergistic collider and redundant collider.
E03 Energy cascade in isotropic turbulence. Causality between the energy transfer at four scales from a direct numerical simulation.
E04 Stochastic systems with dependencies at several time lags, linear and nonlinear.
E05 Moran effect. Two populations driven by a common environmental forcing that is not observed.
E06 Coupled logistic maps with one-way and two-way coupling.
E07 Density estimation with transport maps instead of histograms (needs mpart).
E08 System with eight interacting species. Decomposition with synergies truncated at a given order.
E09 Systems with contemporaneous links on top of mediator, confounder and synergistic interactions.
E10 Experimental data from a turbulent boundary layer. Causality between inner-layer and outer-layer velocity motions as a function of the time lag.

The data for E03 and E10 are in data/. E02 writes the signals it generates to data/ on the first run.

Causality by states

SURD says which variables are causal. surd.states says in which states they are. The method quantifies causality as the information gained about the future states of the target, resolved over the states of the sources and of the target, and splits it into redundant, unique and synergistic contributions as above. A state of a source is causal for a future state of the target when knowing it makes that future state more likely, and non-causal when it makes it less likely. The formulation needs only pairs of past and future events, so it applies to simulations and experiments alike. It handles several variables, dependencies at different time lags and instantaneous links, and it reports the causality that unobserved variables leave unexplained. The method is described in Martínez-Sánchez & Lozano-Durán, Communications Physics 9, 15 (2025).

Causality by states

A source that acts only when it is positive. Un[(1,)][t, s] is the unique causality from state s of the source to state t of the target, so the rows of positive q1 carry it.

import numpy as np
from surd import states

rng = np.random.default_rng(0)
q1, q3 = rng.standard_normal((2, 1_000_000))                                  # two sources
q2 = np.where(q1 > 0, np.sin(q1), 0.0) + 0.1 * rng.standard_normal(q1.size)  # future target: q1 acts only when positive
hist, edges = np.histogramdd(np.vstack([q2, q1, q3]).T, bins=25)              # target first, sources after
I_R, I_S, MI, leak, Rd, Un, Sy = states.surd_states(hist)                     # Rd, Un, Sy: one array per term, indexed by states
states.nice_print(I_R, I_S, MI, leak)
    Redundant (R):
        (1, 2)      : 0.0002
    Unique (U):
        (1,)        : 0.9982
        (2,)        : 0.0000
    Synergystic (S):
        (1, 2)      : 0.0016
    Information Leak: 63.40%
Function What it does
states.surd_states(p) Decomposition of the histogram p (target on axis 0). Returns I_R, I_S, MI and info_leak as surd.surd, plus Rd_states, Un_states and Sy_states: for each pair of sources (or each source for Un_states) an array with the contribution of every combination of target and source states. Unlike surd.surd, no regularization is added to p.
states.run(X, nvars, nlag, nbins, axs) Runs states.surd_states with each signal of X as target at lag nlag, prints and plots the result.
states.plot, states.nice_print Same as their surd counterparts.
states.plot_states(data, bins, target, source, save_path, ...) Map of a state-resolved term over the states of one source and the target, with the marginal sums on top and right.
states.plot_states_3d(data, bins, title, level) Isosurface of a state-resolved term over the target and two sources (needs scikit-image).
states.datasets.load_inner_outer(n_samples) Boundary layer record of example E03 (two signals, 19,990,000 samples, 148 MB). Downloaded once into ~/.cache/surd, or into $SURD_DATA, and checked against its SHA-256.

states.it_tools and states.analytic_eqs are the versions that ship with the paper and differ from surd.it_tools and surd.analytic_eqs: no regularization of the histogram, and the benchmark systems source and target instead of the mediator and collider cases.

The notebooks in examples/states/ reproduce the cases of the paper. Run them from that folder. The marimo/ subfolder has the same examples as marimo notebooks, which also run as plain Python scripts.

Notebook What it shows
E01 Benchmark system where the causality of the sources depends on the state of the first one. Redundant, unique and synergistic maps by states, in two and three dimensions.
E02 Benchmark system where the causality depends on the state of the target.
E03 Experimental data from a turbulent boundary layer. Unique causality from the outer to the inner layer as a function of the time lag, and the decomposition by states at the lag of maximum cross-induced causality. Needs pymp and downloads the data on first run.

E01 and E02 generate their signals and store them in examples/states/data/ on the first run. E01 and E02 need scikit-image for the 3-D maps.

Citation

If you use SURD in your research or software, please cite the paper:

@article{surd,
author={Mart{\'\i}nez-S{\'a}nchez, {\'A}lvaro and Arranz, Gonzalo and Lozano-Dur{\'a}n, Adri{\'a}n},
title={Decomposing causality into its synergistic, unique, and redundant components},
journal={Nature Communications},
year={2024},
month={Nov},
day={01},
volume={15},
number={1},
pages={9296},
issn={2041-1723},
doi={10.1038/s41467-024-53373-4}
}

If you use surd.states, please also cite:

@article{martinez2025,
  author  = {Mart{\'i}nez-S{\'a}nchez, {\'A}lvaro and Lozano-Dur{\'a}n, Adri{\'a}n},
  title   = {Observational causality by states and interaction type for scientific discovery},
  journal = {Communications Physics},
  year    = {2025},
  month   = {Dec},
  day     = {16},
  volume  = {9},
  number  = {1},
  pages   = {15},
  issn    = {2399-3650},
  doi     = {10.1038/s42005-025-02447-w}
}

The file CITATION.cff carries both references in machine-readable form.

Acknowledgements

Enrico Foglia packaged and documented the SURD-states code that became surd.states.

License

This project is licensed under the MIT License. See LICENSE.

Metadata

Release files for surd 1.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for surd 1.2.0
File Size Uploaded Provenance Actions
surd-1.2.0.tar.gz 64.0 kB With provenance Details for surd-1.2.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for surd 1.2.0
File Interpreter ABI Platform Actions
surd-1.2.0-py3-none-any.whl Python 3 none any Details for surd-1.2.0-py3-none-any.whl

Total release size: 96.2 kB

Release files / surd-1.2.0.tar.gz

Download URL surd-1.2.0.tar.gz
Size 64.0 kB
Tags Source
SHA-256 checksum
How to use checksums
e589109f3481aec7ed8bf45e1078e84edae660980ae442dfd4dbc939a460a11f
BLAKE2b-256 checksum
How to use checksums
ca3e589db2c35fec373b095f87746d1e59863ad38087637b8deaa3f13420055a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 30, 2026.

Transparency log

Release files / surd-1.2.0-py3-none-any.whl

Download URL surd-1.2.0-py3-none-any.whl
Size 32.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
4224981c1275a1c9ff4c166cad28e9c183d0718a3942e349af48d1d59c8cfc30
BLAKE2b-256 checksum
How to use checksums
f3d519c537fab26706423ef32ab8f4878e93013a5a7e42a4dba5e7c60c73a9f5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 30, 2026.

Transparency log

Security for surd 1.2.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

Both files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for surd-1.2.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 30, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page