surd 1.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
surd-1.1.0/.gitignore ADDED
@@ -0,0 +1,11 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ .ipynb_checkpoints/
4
+ .DS_Store
5
+ # Packaging and tests
6
+ build/
7
+ dist/
8
+ *.egg-info/
9
+ .pytest_cache/
10
+ # Data generated by the example notebooks
11
+ data/*.npy
@@ -0,0 +1,43 @@
1
+ # Changelog
2
+
3
+ ## 1.1.0 (2026-10-30)
4
+
5
+ - `surd` is an installable package with a `src/` layout and a
6
+ `pyproject.toml`. `pip install .` works from a clone. The public functions
7
+ keep their names, signatures and results: `surd.surd`, `surd.surd_hd`,
8
+ `surd.run`, `surd.run_parallel`, `surd.run_multiple_lags`, `surd.plot`,
9
+ `surd.plot_nlabels`, `surd.plot_multiple_lags` and `surd.nice_print`.
10
+ - `it_tools`, `analytic_eqs` and `transport_map` are submodules
11
+ (`surd.it_tools`, `surd.analytic_eqs`, `surd.transport_map`). The last
12
+ two load on first access, so `import surd` does not need `mpart` and
13
+ does not seed the NumPy random generator.
14
+ - Optional extras: `parallel` (pymp-pypi, Linux and macOS), `transport`
15
+ (mpart) and `examples` (jupyterlab).
16
+ - `utils/` keeps working for scripts that add it to `sys.path`. Its files
17
+ now load the package.
18
+ - The example notebooks import the installed package and fall back to
19
+ `../src` from a plain clone.
20
+ - Test suite (`pytest`) with the logic gates of example E01, the benchmark
21
+ systems of E02, the information-theoretic tools, bounds of the terms and
22
+ the causality leak, `surd.run_parallel` with and without pymp, and the
23
+ equivalence between the `utils/` import path and the package.
24
+ - Continuous integration on Linux, macOS and Windows with Python 3.9 and
25
+ 3.12, and a release workflow that builds and publishes to PyPI.
26
+ - `CITATION.cff` with the Nature Communications paper as preferred
27
+ citation.
28
+ - Portable `environment.yml` (only the packages the code uses) and a
29
+ `requirements.txt` for pip.
30
+ - pymp is optional. Without it, `surd.run_parallel` runs the targets one
31
+ after another and returns the same result. This makes the code run on
32
+ Windows.
33
+ - README rewritten: installation, quick start, table of examples and the
34
+ main functions.
35
+
36
+ ## 1.0.1 (2024-09-11)
37
+
38
+ - Fixes: file names of the turbulent boundary layer data (example E10) and
39
+ README corrections.
40
+
41
+ ## 1.0.0 (2024-09-03)
42
+
43
+ - Initial release.
@@ -0,0 +1,46 @@
1
+ cff-version: 1.2.0
2
+ message: "If you use SURD, please cite the paper below (preferred-citation)."
3
+ type: software
4
+ title: "SURD: Synergistic-Unique-Redundant Decomposition of causality"
5
+ version: 1.1.0
6
+ date-released: 2026-10-30
7
+ license: MIT
8
+ repository-code: "https://github.com/ALD-Lab/SURD"
9
+ url: "https://github.com/ALD-Lab/SURD"
10
+ abstract: >-
11
+ Python package that measures the information that past observations of a
12
+ set of variables carry about the future of a target variable and decomposes
13
+ it into redundant, unique and synergistic contributions, together with the
14
+ causality leak from unobserved variables.
15
+ keywords:
16
+ - causality
17
+ - causal inference
18
+ - information theory
19
+ - synergy
20
+ - redundancy
21
+ - time series
22
+ authors:
23
+ - family-names: "Martínez-Sánchez"
24
+ given-names: "Álvaro"
25
+ - family-names: "Arranz"
26
+ given-names: "Gonzalo"
27
+ - family-names: "Lozano-Durán"
28
+ given-names: "Adrián"
29
+ preferred-citation:
30
+ type: article
31
+ title: "Decomposing causality into its synergistic, unique, and redundant components"
32
+ authors:
33
+ - family-names: "Martínez-Sánchez"
34
+ given-names: "Álvaro"
35
+ - family-names: "Arranz"
36
+ given-names: "Gonzalo"
37
+ - family-names: "Lozano-Durán"
38
+ given-names: "Adrián"
39
+ journal: "Nature Communications"
40
+ year: 2024
41
+ month: 11
42
+ volume: 15
43
+ issue: 1
44
+ start: 9296
45
+ issn: "2041-1723"
46
+ doi: "10.1038/s41467-024-53373-4"
surd-1.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 MIT Computational Turbulence Group
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
surd-1.1.0/PKG-INFO ADDED
@@ -0,0 +1,168 @@
1
+ Metadata-Version: 2.5
2
+ Name: surd
3
+ Version: 1.1.0
4
+ Summary: Synergistic-Unique-Redundant Decomposition of causality for time series
5
+ Project-URL: Repository, https://github.com/ALD-Lab/SURD
6
+ Project-URL: Paper, https://doi.org/10.1038/s41467-024-53373-4
7
+ Project-URL: Changelog, https://github.com/ALD-Lab/SURD/blob/main/CHANGELOG.md
8
+ Project-URL: Issues, https://github.com/ALD-Lab/SURD/issues
9
+ Author: Álvaro Martínez-Sánchez, Gonzalo Arranz, Adrián Lozano-Durán
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: causal inference,causality,information theory,mutual information,redundancy,synergy,time series,turbulence
13
+ Classifier: Development Status :: 5 - Production/Stable
14
+ Classifier: Intended Audience :: Science/Research
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3 :: Only
18
+ Classifier: Programming Language :: Python :: 3.9
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3.13
23
+ Classifier: Topic :: Scientific/Engineering
24
+ Classifier: Topic :: Scientific/Engineering :: Information Analysis
25
+ Classifier: Topic :: Scientific/Engineering :: Mathematics
26
+ Classifier: Topic :: Scientific/Engineering :: Physics
27
+ Requires-Python: >=3.9
28
+ Requires-Dist: matplotlib
29
+ Requires-Dist: numpy
30
+ Requires-Dist: scipy
31
+ Requires-Dist: tqdm
32
+ Provides-Extra: examples
33
+ Requires-Dist: jupyterlab; extra == 'examples'
34
+ Provides-Extra: parallel
35
+ Requires-Dist: pymp-pypi; (platform_system != 'Windows') and extra == 'parallel'
36
+ Provides-Extra: transport
37
+ Requires-Dist: mpart; extra == 'transport'
38
+ Description-Content-Type: text/markdown
39
+
40
+ # SURD: Synergistic-Unique-Redundant Decomposition of causality
41
+
42
+ [![tests](https://github.com/ALD-Lab/SURD/actions/workflows/tests.yml/badge.svg)](https://github.com/ALD-Lab/SURD/actions/workflows/tests.yml)
43
+ [![PyPI](https://img.shields.io/pypi/v/surd.svg)](https://pypi.org/project/surd/)
44
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
45
+ [![Paper](https://img.shields.io/badge/Nat.%20Commun.-10.1038%2Fs41467--024--53373--4-blue)](https://doi.org/10.1038/s41467-024-53373-4)
46
+
47
+ 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).
48
+
49
+ ![SURD diagram](https://github.com/user-attachments/assets/937fa18d-1179-4cfe-ae5a-e262582614a0)
50
+
51
+ ## Installation
52
+
53
+ From PyPI:
54
+ ```sh
55
+ pip install surd
56
+ ```
57
+
58
+ From a clone of this repository:
59
+ ```sh
60
+ git clone https://github.com/ALD-Lab/SURD.git
61
+ cd SURD
62
+ pip install .
63
+ ```
64
+
65
+ With conda, the environment file installs the dependencies and the package:
66
+ ```sh
67
+ conda env create -f environment.yml
68
+ conda activate surd
69
+ ```
70
+
71
+ SURD depends on NumPy, SciPy, Matplotlib and tqdm and runs on Python 3.9 or newer, on Linux, macOS and Windows. Three optional extras exist:
72
+
73
+ - `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.
74
+ - `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](https://measuretransport.github.io/MParT/).
75
+ - `pip install "surd[examples]"` adds JupyterLab to run the notebooks.
76
+
77
+ 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.
78
+
79
+ ## Quick start
80
+
81
+ 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.
82
+
83
+ ```python
84
+ import numpy as np
85
+ import surd
86
+
87
+ rng = np.random.default_rng(0)
88
+ q1, q2 = rng.integers(0, 2, size=(2, 200_000)) # two independent bits in time
89
+ target = np.roll(q1 ^ q2, 1) # their XOR, one step later
90
+ Y = np.vstack([target[1:], q1[:-1], q2[:-1]]) # future of the target first, sources after
91
+ hist, _ = np.histogramdd(Y.T, bins=2)
92
+ I_R, I_S, MI, info_leak = surd.surd(hist)
93
+ surd.nice_print(I_R, I_S, MI, info_leak)
94
+ ```
95
+
96
+ ```
97
+ Redundant (R):
98
+ (1, 2) : 0.0000
99
+ Unique (U):
100
+ (1,) : 0.0000
101
+ (2,) : 0.0000
102
+ Synergystic (S):
103
+ (1, 2) : 1.0000
104
+ Information Leak: 0.00%
105
+ ```
106
+
107
+ ## How to use the method
108
+
109
+ 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.
110
+
111
+ | Function | What it does |
112
+ |---|---|
113
+ | `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]. |
114
+ | `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`. |
115
+ | `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. |
116
+ | `surd.run_parallel(...)` | Same as `surd.run`, with the targets in parallel when `pymp` is available. |
117
+ | `surd.run_multiple_lags(X, nvars, nlag, nbins, max_combs, axs)` | Same as `surd.run` with all lags from 1 to `nlag` as sources. |
118
+ | `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. |
119
+ | `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. |
120
+
121
+ `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`.
122
+
123
+ 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.
124
+
125
+ ## Examples
126
+
127
+ The notebooks in [`examples/`](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.
128
+
129
+ | Notebook | What it shows |
130
+ |---|---|
131
+ | [E01](examples/E01_logic_gates.ipynb) | Logic gates. A duplicated source is redundant, a copied source is unique, an XOR is synergistic. |
132
+ | [E02](examples/E02_building_blocks.ipynb) | Building blocks of causal interactions: mediator, confounder, synergistic collider and redundant collider. |
133
+ | [E03](examples/E03_energy_cascade.ipynb) | Energy cascade in isotropic turbulence. Causality between the energy transfer at four scales from a direct numerical simulation. |
134
+ | [E04](examples/E04_lagged_dependencies.ipynb) | Stochastic systems with dependencies at several time lags, linear and nonlinear. |
135
+ | [E05](examples/E05_Moran_effect.ipynb) | Moran effect. Two populations driven by a common environmental forcing that is not observed. |
136
+ | [E06](examples/E06_logistic_system.ipynb) | Coupled logistic maps with one-way and two-way coupling. |
137
+ | [E07](examples/E07_transport_map.ipynb) | Density estimation with transport maps instead of histograms (needs `mpart`). |
138
+ | [E08](examples/E08_eight_species.ipynb) | System with eight interacting species. Decomposition with synergies truncated at a given order. |
139
+ | [E09](examples/E09_contemporaneous.ipynb) | Systems with contemporaneous links on top of mediator, confounder and synergistic interactions. |
140
+ | [E10](examples/E10_inner_outer.ipynb) | Experimental data from a turbulent boundary layer. Causality between inner-layer and outer-layer velocity motions as a function of the time lag. |
141
+
142
+ The data for E03 and E10 are in [`data/`](data). E02 writes the signals it generates to `data/` on the first run.
143
+
144
+ ## Citation
145
+
146
+ If you use SURD in your research or software, please cite the paper:
147
+
148
+ ```bibtex
149
+ @article{surd,
150
+ author={Mart{\'\i}nez-S{\'a}nchez, {\'A}lvaro and Arranz, Gonzalo and Lozano-Dur{\'a}n, Adri{\'a}n},
151
+ title={Decomposing causality into its synergistic, unique, and redundant components},
152
+ journal={Nature Communications},
153
+ year={2024},
154
+ month={Nov},
155
+ day={01},
156
+ volume={15},
157
+ number={1},
158
+ pages={9296},
159
+ issn={2041-1723},
160
+ doi={10.1038/s41467-024-53373-4}
161
+ }
162
+ ```
163
+
164
+ The file [`CITATION.cff`](CITATION.cff) carries the same reference in machine-readable form.
165
+
166
+ ## License
167
+
168
+ This project is licensed under the MIT License. See [LICENSE](LICENSE).
surd-1.1.0/README.md ADDED
@@ -0,0 +1,129 @@
1
+ # SURD: Synergistic-Unique-Redundant Decomposition of causality
2
+
3
+ [![tests](https://github.com/ALD-Lab/SURD/actions/workflows/tests.yml/badge.svg)](https://github.com/ALD-Lab/SURD/actions/workflows/tests.yml)
4
+ [![PyPI](https://img.shields.io/pypi/v/surd.svg)](https://pypi.org/project/surd/)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
6
+ [![Paper](https://img.shields.io/badge/Nat.%20Commun.-10.1038%2Fs41467--024--53373--4-blue)](https://doi.org/10.1038/s41467-024-53373-4)
7
+
8
+ 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).
9
+
10
+ ![SURD diagram](https://github.com/user-attachments/assets/937fa18d-1179-4cfe-ae5a-e262582614a0)
11
+
12
+ ## Installation
13
+
14
+ From PyPI:
15
+ ```sh
16
+ pip install surd
17
+ ```
18
+
19
+ From a clone of this repository:
20
+ ```sh
21
+ git clone https://github.com/ALD-Lab/SURD.git
22
+ cd SURD
23
+ pip install .
24
+ ```
25
+
26
+ With conda, the environment file installs the dependencies and the package:
27
+ ```sh
28
+ conda env create -f environment.yml
29
+ conda activate surd
30
+ ```
31
+
32
+ SURD depends on NumPy, SciPy, Matplotlib and tqdm and runs on Python 3.9 or newer, on Linux, macOS and Windows. Three optional extras exist:
33
+
34
+ - `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.
35
+ - `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](https://measuretransport.github.io/MParT/).
36
+ - `pip install "surd[examples]"` adds JupyterLab to run the notebooks.
37
+
38
+ 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.
39
+
40
+ ## Quick start
41
+
42
+ 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.
43
+
44
+ ```python
45
+ import numpy as np
46
+ import surd
47
+
48
+ rng = np.random.default_rng(0)
49
+ q1, q2 = rng.integers(0, 2, size=(2, 200_000)) # two independent bits in time
50
+ target = np.roll(q1 ^ q2, 1) # their XOR, one step later
51
+ Y = np.vstack([target[1:], q1[:-1], q2[:-1]]) # future of the target first, sources after
52
+ hist, _ = np.histogramdd(Y.T, bins=2)
53
+ I_R, I_S, MI, info_leak = surd.surd(hist)
54
+ surd.nice_print(I_R, I_S, MI, info_leak)
55
+ ```
56
+
57
+ ```
58
+ Redundant (R):
59
+ (1, 2) : 0.0000
60
+ Unique (U):
61
+ (1,) : 0.0000
62
+ (2,) : 0.0000
63
+ Synergystic (S):
64
+ (1, 2) : 1.0000
65
+ Information Leak: 0.00%
66
+ ```
67
+
68
+ ## How to use the method
69
+
70
+ 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.
71
+
72
+ | Function | What it does |
73
+ |---|---|
74
+ | `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]. |
75
+ | `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`. |
76
+ | `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. |
77
+ | `surd.run_parallel(...)` | Same as `surd.run`, with the targets in parallel when `pymp` is available. |
78
+ | `surd.run_multiple_lags(X, nvars, nlag, nbins, max_combs, axs)` | Same as `surd.run` with all lags from 1 to `nlag` as sources. |
79
+ | `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. |
80
+ | `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. |
81
+
82
+ `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`.
83
+
84
+ 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.
85
+
86
+ ## Examples
87
+
88
+ The notebooks in [`examples/`](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.
89
+
90
+ | Notebook | What it shows |
91
+ |---|---|
92
+ | [E01](examples/E01_logic_gates.ipynb) | Logic gates. A duplicated source is redundant, a copied source is unique, an XOR is synergistic. |
93
+ | [E02](examples/E02_building_blocks.ipynb) | Building blocks of causal interactions: mediator, confounder, synergistic collider and redundant collider. |
94
+ | [E03](examples/E03_energy_cascade.ipynb) | Energy cascade in isotropic turbulence. Causality between the energy transfer at four scales from a direct numerical simulation. |
95
+ | [E04](examples/E04_lagged_dependencies.ipynb) | Stochastic systems with dependencies at several time lags, linear and nonlinear. |
96
+ | [E05](examples/E05_Moran_effect.ipynb) | Moran effect. Two populations driven by a common environmental forcing that is not observed. |
97
+ | [E06](examples/E06_logistic_system.ipynb) | Coupled logistic maps with one-way and two-way coupling. |
98
+ | [E07](examples/E07_transport_map.ipynb) | Density estimation with transport maps instead of histograms (needs `mpart`). |
99
+ | [E08](examples/E08_eight_species.ipynb) | System with eight interacting species. Decomposition with synergies truncated at a given order. |
100
+ | [E09](examples/E09_contemporaneous.ipynb) | Systems with contemporaneous links on top of mediator, confounder and synergistic interactions. |
101
+ | [E10](examples/E10_inner_outer.ipynb) | Experimental data from a turbulent boundary layer. Causality between inner-layer and outer-layer velocity motions as a function of the time lag. |
102
+
103
+ The data for E03 and E10 are in [`data/`](data). E02 writes the signals it generates to `data/` on the first run.
104
+
105
+ ## Citation
106
+
107
+ If you use SURD in your research or software, please cite the paper:
108
+
109
+ ```bibtex
110
+ @article{surd,
111
+ author={Mart{\'\i}nez-S{\'a}nchez, {\'A}lvaro and Arranz, Gonzalo and Lozano-Dur{\'a}n, Adri{\'a}n},
112
+ title={Decomposing causality into its synergistic, unique, and redundant components},
113
+ journal={Nature Communications},
114
+ year={2024},
115
+ month={Nov},
116
+ day={01},
117
+ volume={15},
118
+ number={1},
119
+ pages={9296},
120
+ issn={2041-1723},
121
+ doi={10.1038/s41467-024-53373-4}
122
+ }
123
+ ```
124
+
125
+ The file [`CITATION.cff`](CITATION.cff) carries the same reference in machine-readable form.
126
+
127
+ ## License
128
+
129
+ This project is licensed under the MIT License. See [LICENSE](LICENSE).
@@ -0,0 +1,86 @@
1
+ [build-system]
2
+ requires = ["hatchling>=1.27"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "surd"
7
+ dynamic = ["version"]
8
+ description = "Synergistic-Unique-Redundant Decomposition of causality for time series"
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ license-files = ["LICENSE"]
12
+ requires-python = ">=3.9"
13
+ authors = [
14
+ { name = "Álvaro Martínez-Sánchez" },
15
+ { name = "Gonzalo Arranz" },
16
+ { name = "Adrián Lozano-Durán" },
17
+ ]
18
+ keywords = [
19
+ "causality",
20
+ "causal inference",
21
+ "information theory",
22
+ "mutual information",
23
+ "synergy",
24
+ "redundancy",
25
+ "time series",
26
+ "turbulence",
27
+ ]
28
+ classifiers = [
29
+ "Development Status :: 5 - Production/Stable",
30
+ "Intended Audience :: Science/Research",
31
+ "Operating System :: OS Independent",
32
+ "Programming Language :: Python :: 3",
33
+ "Programming Language :: Python :: 3 :: Only",
34
+ "Programming Language :: Python :: 3.9",
35
+ "Programming Language :: Python :: 3.10",
36
+ "Programming Language :: Python :: 3.11",
37
+ "Programming Language :: Python :: 3.12",
38
+ "Programming Language :: Python :: 3.13",
39
+ "Topic :: Scientific/Engineering",
40
+ "Topic :: Scientific/Engineering :: Information Analysis",
41
+ "Topic :: Scientific/Engineering :: Mathematics",
42
+ "Topic :: Scientific/Engineering :: Physics",
43
+ ]
44
+ dependencies = [
45
+ "numpy",
46
+ "scipy",
47
+ "matplotlib",
48
+ "tqdm",
49
+ ]
50
+
51
+ [project.optional-dependencies]
52
+ # Parallel loop over target variables in surd.run_parallel (not on Windows).
53
+ parallel = ["pymp-pypi; platform_system != 'Windows'"]
54
+ # Density estimation with transport maps (surd.transport_map, example E07).
55
+ transport = ["mpart"]
56
+ # Running the example notebooks.
57
+ examples = ["jupyterlab"]
58
+
59
+ [project.urls]
60
+ Repository = "https://github.com/ALD-Lab/SURD"
61
+ Paper = "https://doi.org/10.1038/s41467-024-53373-4"
62
+ Changelog = "https://github.com/ALD-Lab/SURD/blob/main/CHANGELOG.md"
63
+ Issues = "https://github.com/ALD-Lab/SURD/issues"
64
+
65
+ [tool.hatch.version]
66
+ path = "src/surd/__init__.py"
67
+
68
+ [tool.hatch.build.targets.wheel]
69
+ packages = ["src/surd"]
70
+
71
+ [tool.hatch.build.targets.sdist]
72
+ # The data files and the executed notebooks are large and stay in the
73
+ # repository; the source distribution carries only the package and its tests.
74
+ only-include = [
75
+ "src/surd",
76
+ "tests",
77
+ "CHANGELOG.md",
78
+ "CITATION.cff",
79
+ ]
80
+
81
+ [tool.pytest.ini_options]
82
+ testpaths = ["tests"]
83
+ addopts = "-ra"
84
+ filterwarnings = [
85
+ "ignore::DeprecationWarning:pymp",
86
+ ]
@@ -0,0 +1,77 @@
1
+ """SURD: Synergistic-Unique-Redundant Decomposition of causality.
2
+
3
+ SURD measures the information that past observations of a set of source
4
+ variables carry about the future of a target variable and splits it into
5
+ redundant, unique and synergistic contributions. The remaining uncertainty
6
+ is the causality leak from unobserved variables.
7
+
8
+ Reference: Martínez-Sánchez, Arranz & Lozano-Durán, "Decomposing causality
9
+ into its synergistic, unique, and redundant components", Nature
10
+ Communications 15, 9296 (2024). https://doi.org/10.1038/s41467-024-53373-4
11
+
12
+ Typical use::
13
+
14
+ import numpy as np
15
+ import surd
16
+
17
+ hist, _ = np.histogramdd(Y.T, bins) # Y[0] target, Y[1:] sources
18
+ I_R, I_S, MI, info_leak = surd.surd(hist)
19
+ surd.nice_print(I_R, I_S, MI, info_leak)
20
+
21
+ Submodules:
22
+
23
+ - ``surd.it_tools``: entropies, mutual information and transfer entropy
24
+ on discrete distributions.
25
+ - ``surd.analytic_eqs``: the stochastic benchmark systems of the paper
26
+ (mediator, confounder, synergistic and redundant colliders).
27
+ - ``surd.transport_map``: density estimation with triangular transport
28
+ maps. Imported on first access and needs the optional ``mpart`` package.
29
+ """
30
+
31
+ import importlib
32
+
33
+ from . import it_tools
34
+ from .core import (
35
+ nice_print,
36
+ plot,
37
+ plot_multiple_lags,
38
+ plot_nlabels,
39
+ run,
40
+ run_multiple_lags,
41
+ run_parallel,
42
+ surd,
43
+ surd_hd,
44
+ )
45
+
46
+ __version__ = "1.1.0"
47
+
48
+ __all__ = [
49
+ "surd",
50
+ "surd_hd",
51
+ "run",
52
+ "run_parallel",
53
+ "run_multiple_lags",
54
+ "plot",
55
+ "plot_nlabels",
56
+ "plot_multiple_lags",
57
+ "nice_print",
58
+ "it_tools",
59
+ "analytic_eqs",
60
+ "transport_map",
61
+ "__version__",
62
+ ]
63
+
64
+ # Submodules loaded on first access. ``analytic_eqs`` seeds NumPy's global
65
+ # random generator when imported, and ``transport_map`` needs ``mpart``,
66
+ # so neither is imported by a plain ``import surd``.
67
+ _LAZY_SUBMODULES = ("analytic_eqs", "transport_map")
68
+
69
+
70
+ def __getattr__(name):
71
+ if name in _LAZY_SUBMODULES:
72
+ return importlib.import_module(f"{__name__}.{name}")
73
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
74
+
75
+
76
+ def __dir__():
77
+ return sorted(set(globals()) | set(_LAZY_SUBMODULES))
@@ -0,0 +1,39 @@
1
+ import numpy as np
2
+ np.random.seed(10)
3
+
4
+ def mediator(N):
5
+ q1, q2, q3 = np.zeros(N), np.zeros(N), np.zeros(N)
6
+ W1, W2, W3 = np.random.normal(0, 1, N), np.random.normal(0, 1, N), np.random.normal(0, 1, N)
7
+ for n in range(N-1):
8
+ q1[n+1] = np.sin(q2[n]) + 0.001*W1[n]
9
+ q2[n+1] = np.cos(q3[n]) + 0.01*W2[n]
10
+ q3[n+1] = 0.5*q3[n] + 0.1*W3[n]
11
+ return q1, q2, q3
12
+
13
+ def confounder(N):
14
+ q1, q2, q3 = np.zeros(N), np.zeros(N), np.zeros(N)
15
+ W1, W2, W3 = np.random.normal(0, 1, N), np.random.normal(0, 1, N), np.random.normal(0, 1, N)
16
+ for n in range(N-1):
17
+ q1[n+1] = np.sin(q1[n] + q3[n]) + 0.01*W1[n]
18
+ q2[n+1] = np.cos(q2[n] - q3[n]) + 0.01*W2[n]
19
+ q3[n+1] = 0.5*q3[n] + 0.1*W3[n]
20
+ return q1, q2, q3
21
+
22
+ def synergistic_collider(N):
23
+ q1, q2, q3 = np.zeros(N), np.zeros(N), np.zeros(N)
24
+ W1, W2, W3 = np.random.normal(0, 1, N), np.random.normal(0, 1, N), np.random.normal(0, 1, N)
25
+ for n in range(N-1):
26
+ q1[n+1] = np.sin(q2[n] * q3[n]) + 0.001*W1[n]
27
+ q2[n+1] = 0.5*q2[n] + 0.1*W2[n]
28
+ q3[n+1] = 0.5*q3[n] + 0.1*W3[n]
29
+ return q1, q2, q3
30
+
31
+ def redundant_collider(N):
32
+ q1, q2, q3 = np.zeros(N), np.zeros(N), np.zeros(N)
33
+ W1, W2, W3 = np.random.normal(0, 1, N), np.random.normal(0, 1, N), np.random.normal(0, 1, N)
34
+ for n in range(N-1):
35
+ # q1[n+1] = 0.3*q1[n] + 0.7*(np.sin(q2[n]*q3[n]) + 0.1*W1[n])
36
+ q1[n+1] = 0.3*q1[n] + (np.sin(q2[n]*q3[n]) + 0.001*W1[n])
37
+ q2[n+1] = 0.5*q2[n] + 0.1*W2[n]
38
+ q3[n+1] = q2[n+1]
39
+ return q1, q2, q3