From 194fd0dfd8ae7df0a4c2a8319eead24ef79be54b Mon Sep 17 00:00:00 2001 From: "David A. Ehrlich" Date: Thu, 18 Jan 2024 10:22:46 +0100 Subject: [PATCH 01/34] Fix: Corrected unsorting in fdr test --- idtxl/stats.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/idtxl/stats.py b/idtxl/stats.py index 5c2fc21..8174e70 100644 --- a/idtxl/stats.py +++ b/idtxl/stats.py @@ -297,7 +297,7 @@ def _perform_fdr_corretion(pval, constant, alpha): if np.invert(sign).any(): first_false = np.where(np.invert(sign))[0][0] sign[first_false:] = False # avoids false positives due to equal pvals - sign = sign[sort_idx] # restore original ordering of significance values + sign[sort_idx] = sign.copy() # restore original ordering of significance values return sign, thresh @@ -687,8 +687,8 @@ def max_statistic_sequential(analysis_setup, data): break # Get back original order and return results. - significance = significance[selected_vars_order] - pvalue = pvalue[selected_vars_order] + significance[selected_vars_order] = significance.copy() + pvalue[selected_vars_order] = pvalue.copy() return significance, pvalue, individual_stat From a0e735e0d3b6020f1c46ef3e20da9883f3d42102 Mon Sep 17 00:00:00 2001 From: "David A. Ehrlich" Date: Thu, 18 Jan 2024 12:17:57 +0100 Subject: [PATCH 02/34] Fix: Removed filtering by omnibus significance in fdr correction --- idtxl/stats.py | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/idtxl/stats.py b/idtxl/stats.py index 8174e70..76e4c18 100644 --- a/idtxl/stats.py +++ b/idtxl/stats.py @@ -163,22 +163,26 @@ def network_fdr(settings=None, *results): cands = [] if correct_by_target: # whole target for target in results_comb.targets_analysed: - if results_comb._single_target[target].omnibus_sign: - pval = np.append(pval, results_comb._single_target[target].omnibus_pval) - target_idx = np.append(target_idx, target) - n_perm = np.append(n_perm, results_comb.settings.n_perm_omnibus) + next_pval = results_comb._single_target[target].omnibus_pval + pval = np.append( + pval, next_pval if next_pval is not None else 1) + target_idx = np.append(target_idx, target) + n_perm = np.append( + n_perm, results_comb.settings.n_perm_omnibus) else: # individual variables for target in results_comb.targets_analysed: - if results_comb._single_target[target].omnibus_sign: - n_sign = results_comb._single_target[target].selected_sources_pval.size - pval = np.append( - pval, (results_comb._single_target[target].selected_sources_pval) - ) - target_idx = np.append(target_idx, np.ones(n_sign) * target).astype(int) - cands = cands + ( - results_comb._single_target[target].selected_vars_sources - ) - n_perm = np.append(n_perm, results_comb.settings.n_perm_max_seq) + n_sign = (results_comb._single_target[target]. + selected_sources_pval.size) + pval = np.append( + pval, (results_comb._single_target[target]. + selected_sources_pval)) + target_idx = np.append(target_idx, + np.ones(n_sign) * target).astype(int) + cands = (cands + + (results_comb._single_target[target]. + selected_vars_sources)) + n_perm = np.append( + n_perm, results_comb.settings.n_perm_max_seq) if pval.size == 0: print("No links in final results ...") From 3eb63049fa6d3013b6b8a05a5e9e36cacf5b0ed5 Mon Sep 17 00:00:00 2001 From: "David A. Ehrlich" Date: Thu, 25 Jan 2024 17:20:29 +0100 Subject: [PATCH 03/34] Fix: Removed filtering in fdr correction for AIS --- idtxl/stats.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/idtxl/stats.py b/idtxl/stats.py index 76e4c18..e54bff0 100644 --- a/idtxl/stats.py +++ b/idtxl/stats.py @@ -59,10 +59,10 @@ def ais_fdr(settings=None, *results): process_idx = np.arange(0).astype(int) n_perm = np.arange(0).astype(int) for process in results_comb.processes_analysed: - if results_comb._single_process[process].ais_sign: - pval = np.append(pval, results_comb._single_process[process].ais_pval) - process_idx = np.append(process_idx, process) - n_perm = np.append(n_perm, results_comb.settings.n_perm_mi) + next_pval = results_comb._single_process[process].ais_pval + pval = np.append(pval, next_pval if next_pval is not None else 1) + process_idx = np.append(process_idx, process) + n_perm = np.append(n_perm, results_comb.settings.n_perm_mi) if pval.size == 0: print("FDR correction: no links in final results ...\n") From 1330787f30250d95ed13f734972025084b057ea5 Mon Sep 17 00:00:00 2001 From: "David A. Ehrlich" Date: Thu, 25 Jan 2024 17:25:50 +0100 Subject: [PATCH 04/34] Fix: Correctly compute the total number of tests for correct_by_target==False --- idtxl/stats.py | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/idtxl/stats.py b/idtxl/stats.py index e54bff0..d1467b0 100644 --- a/idtxl/stats.py +++ b/idtxl/stats.py @@ -69,7 +69,7 @@ def ais_fdr(settings=None, *results): results_comb._add_fdr(fdr=None, alpha=alpha, constant=constant) return results_comb - sign, thresh = _perform_fdr_corretion(pval, constant, alpha) + sign, thresh = _perform_fdr_corretion(pval, constant, alpha, len(results_comb.processes_analysed)) # If the number of permutations for calculating p-values for individual # variables is too low, return without performing any correction. @@ -162,6 +162,9 @@ def network_fdr(settings=None, *results): n_perm = np.arange(0).astype(int) cands = [] if correct_by_target: # whole target + # The total number of tests is the number of targets + n_tests = len(results_comb.targets_analysed) + for target in results_comb.targets_analysed: next_pval = results_comb._single_target[target].omnibus_pval pval = np.append( @@ -170,12 +173,17 @@ def network_fdr(settings=None, *results): n_perm = np.append( n_perm, results_comb.settings.n_perm_omnibus) else: # individual variables + # The total number of tests is the number of targets times the number + # of source candidates (i.e. source processes * time lags) analyzed for each target + n_tests = np.sum(len(target.source_set) for target in results.targets_analysed) \ + * (settings["max_lag_sources"] - settings["min_lag_sources"] + 1) + for target in results_comb.targets_analysed: n_sign = (results_comb._single_target[target]. selected_sources_pval.size) pval = np.append( - pval, (results_comb._single_target[target]. - selected_sources_pval)) + pval, [next_pval if next_pval is not None else 1 for next_pval in + results_comb._single_target[target].selected_sources_pval]) target_idx = np.append(target_idx, np.ones(n_sign) * target).astype(int) cands = (cands + @@ -194,7 +202,7 @@ def network_fdr(settings=None, *results): ) return results_comb - sign, thresh = _perform_fdr_corretion(pval, constant, alpha) + sign, thresh = _perform_fdr_corretion(pval, constant, alpha, n_tests) # If the number of permutations for calculating p-values for individual # variables is too low, return without performing any correction. @@ -247,7 +255,7 @@ def network_fdr(settings=None, *results): return results_comb -def _perform_fdr_corretion(pval, constant, alpha): +def _perform_fdr_corretion(pval, constant, alpha, n_tests): """Calculate sequential threshold for FDR-correction. Calculate sequential thresholds for FDR-correction of p-values. The @@ -270,31 +278,34 @@ def _perform_fdr_corretion(pval, constant, alpha): according to Genovese (2002): 1 will divide alpha by 1, 2 will divide alpha by the sum_i(1/i); see the paper for details on the assumptions (default=2) + n_tests : int + total number of tests performed for calculating the FDR-thresholds Returns: array of bools - significance of p-values + significance of p-values in the order of the input array array of floats - FDR-thresholds for each p-value + FDR-thresholds for each p-value in increasing order """ # Sort all p-values in ascending order. sort_idx = np.argsort(pval) pval.sort() # Calculate threshold - n = pval.size if constant == 2: # pick the requested constant (see Genovese, p.872) - if n < 1000: - const = sum(1 / np.arange(1, n + 1)) + if n_tests < 1000: + const = np.sum(1 / np.arange(1, n_tests + 1)) else: - const = np.log(n) + np.e # aprx. harmonic sum with Euler's number + const = np.log(n_tests) + np.e # aprx. harmonic sum with Euler's number elif constant == 1: # This is less strict than the other one and corresponds to a # Bonoferroni-correction for the first p-value, however, it makes more # strict assumptions on the distribution of p-values, while constant 2 # works for any joint distribution of the p-values. const = 1 - thresh = (np.arange(1, n + 1) / n) * alpha / const + + # Calculate threshold for each p-value. + thresh = (np.arange(1, len(pval) + 1) / n_tests) * alpha / const # Compare data to threshold. sign = pval <= thresh From 84c88ff0ab0ed04b185f9bf702a6e84d7b0259d3 Mon Sep 17 00:00:00 2001 From: Patricia Wollstadt Date: Sat, 27 Jan 2024 16:54:33 +0100 Subject: [PATCH 05/34] Fix input checks in multivariate network inference --- idtxl/multivariate_mi.py | 18 ++++++++---------- idtxl/multivariate_te.py | 26 ++++++++++---------------- 2 files changed, 18 insertions(+), 26 deletions(-) diff --git a/idtxl/multivariate_mi.py b/idtxl/multivariate_mi.py index 26d709b..0c4c244 100644 --- a/idtxl/multivariate_mi.py +++ b/idtxl/multivariate_mi.py @@ -7,9 +7,9 @@ Note: Written for Python 3.4+ """ -from .stats import network_fdr from .network_inference import NetworkInferenceMI, NetworkInferenceMultivariate from .results import ResultsNetworkInference +from .stats import network_fdr class MultivariateMI(NetworkInferenceMI, NetworkInferenceMultivariate): @@ -129,20 +129,18 @@ def analyse_network(self, settings, data, targets="all", sources="all"): # Check which targets and sources are requested for analysis. if targets == "all": - targets = [t for t in range(data.n_processes)] + targets = list(range(data.n_processes)) if sources == "all": sources = ["all" for t in targets] - if (type(sources) is list) and (type(sources[0]) is int): + elif isinstance(sources, list) and isinstance(sources[0], int): sources = [sources for t in targets] - if (type(sources) is list) and (type(sources[0]) is list): + elif isinstance(sources, list) and isinstance(sources[0], list): pass else: - ValueError("Sources was not specified correctly: {0}.".format(sources)) - assert len(sources) == len(targets), ( - "List of targets and list of " - "sources have to have the same " - "same length" - ) + raise ValueError(f"Sources was not specified correctly: {sources}.") + assert len(sources) == len( + targets + ), "List of targets and list of sources have to have the same length" # Check and set defaults for checkpointing. settings = self._set_checkpointing_defaults(settings, data, sources, targets) diff --git a/idtxl/multivariate_te.py b/idtxl/multivariate_te.py index db8198a..b727b3f 100755 --- a/idtxl/multivariate_te.py +++ b/idtxl/multivariate_te.py @@ -7,9 +7,9 @@ Note: Written for Python 3.4+ """ -from .network_inference import NetworkInferenceTE, NetworkInferenceMultivariate -from .stats import network_fdr +from .network_inference import NetworkInferenceMultivariate, NetworkInferenceTE from .results import ResultsNetworkInference +from .stats import network_fdr class MultivariateTE(NetworkInferenceTE, NetworkInferenceMultivariate): @@ -132,20 +132,18 @@ def analyse_network(self, settings, data, targets="all", sources="all"): # Check which targets and sources are requested for analysis. if targets == "all": - targets = [t for t in range(data.n_processes)] + targets = list(range(data.n_processes)) if sources == "all": sources = ["all" for t in targets] - if (type(sources) is list) and (type(sources[0]) is int): + elif isinstance(sources is list) and isinstance(sources[0], int): sources = [sources for t in targets] - if (type(sources) is list) and (type(sources[0]) is list): + elif isinstance(sources, list) and isinstance(sources[0], list): pass else: - ValueError("Sources was not specified correctly: {0}.".format(sources)) - assert len(sources) == len(targets), ( - "List of targets and list of " - "sources have to have the same " - "same length" - ) + raise ValueError(f"Sources was not specified correctly: {sources}.") + assert len(sources) == len( + targets + ), "List of targets and list of sources have to have the same length" # Check and set defaults for checkpointing. settings = self._set_checkpointing_defaults(settings, data, sources, targets) @@ -158,11 +156,7 @@ def analyse_network(self, settings, data, targets="all", sources="all"): ) for t in range(len(targets)): if settings["verbose"]: - print( - "\n####### analysing target with index {0} from list {1}".format( - t, targets - ) - ) + print(f"\n####### analysing target with index {t} from list {targets}") res_single = self.analyse_single_target( settings, data, targets[t], sources[t] ) From e1ac96bab0b58357b984c9320b9ceac8913153e9 Mon Sep 17 00:00:00 2001 From: Patricia Wollstadt Date: Sat, 27 Jan 2024 17:00:05 +0100 Subject: [PATCH 06/34] Update version number --- docs/conf.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 838f6e6..1611d4d 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -71,7 +71,7 @@ # built documents. # # The short X.Y version. -version = "1.5.1" +version = "1.6.0" # The full version, including alpha/beta/rc tags. release = version diff --git a/setup.py b/setup.py index 301f05e..6197c07 100644 --- a/setup.py +++ b/setup.py @@ -8,7 +8,7 @@ name="idtxl", packages=["idtxl"], include_package_data=True, - version="1.5.1", + version="1.6", description="Information Dynamics Toolkit xl", author="Patricia Wollstadt, Joseph T. Lizier, Raul Vicente, Conor Finn, Mario Martinez-Zarzuela, Pedro Mediano, Leonardo Novelli, Michael Wibral", author_email="p.wollstadt@gmail.com", From cb959689878ae38c0ee407ba1480e37e1d0df4d6 Mon Sep 17 00:00:00 2001 From: Patricia Wollstadt Date: Mon, 29 Jan 2024 08:06:45 +0100 Subject: [PATCH 07/34] Fix calculation of no. tests for candidate-wise FDR Correctly calculate the number of tests when running FDR per candidate test, i.e., with correct_by_target=False. Fix formatting. --- idtxl/stats.py | 68 +++++++++++++++++++++++++------------------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/idtxl/stats.py b/idtxl/stats.py index d1467b0..1169032 100644 --- a/idtxl/stats.py +++ b/idtxl/stats.py @@ -1,8 +1,11 @@ """Provide statistics functions.""" +# pylint: disable=protected-access import copy as cp + import numpy as np -from . import idtxl_utils as utils + from . import idtxl_exceptions as ex +from . import idtxl_utils as utils def ais_fdr(settings=None, *results): @@ -69,18 +72,16 @@ def ais_fdr(settings=None, *results): results_comb._add_fdr(fdr=None, alpha=alpha, constant=constant) return results_comb - sign, thresh = _perform_fdr_corretion(pval, constant, alpha, len(results_comb.processes_analysed)) + sign, thresh = _perform_fdr_corretion( + pval, constant, alpha, len(results_comb.processes_analysed) + ) # If the number of permutations for calculating p-values for individual # variables is too low, return without performing any correction. if (1 / min(n_perm)) > thresh[0]: print( - "WARNING: Number of permutations (" - "n_perm_max_seq" - ") for at " - "least one target is too low to allow for FDR correction " - "(FDR-threshold: {0:.4f}, min. theoretically possible p-value: " - "{1}).".format(thresh[0], 1 / min(n_perm)) + "WARNING: Number of permutations ('n_perm_max_seq') for at least one target is too low to allow for " + f"FDR correction (FDR-threshold: {thresh[0]:.4f}, min. theoretically possible p-value: {1 / min(n_perm)})." ) results_comb._add_fdr(fdr=None, alpha=alpha, constant=constant) return results_comb @@ -167,30 +168,33 @@ def network_fdr(settings=None, *results): for target in results_comb.targets_analysed: next_pval = results_comb._single_target[target].omnibus_pval - pval = np.append( - pval, next_pval if next_pval is not None else 1) + pval = np.append(pval, next_pval if next_pval is not None else 1) target_idx = np.append(target_idx, target) - n_perm = np.append( - n_perm, results_comb.settings.n_perm_omnibus) + n_perm = np.append(n_perm, results_comb.settings.n_perm_omnibus) else: # individual variables # The total number of tests is the number of targets times the number # of source candidates (i.e. source processes * time lags) analyzed for each target - n_tests = np.sum(len(target.source_set) for target in results.targets_analysed) \ - * (settings["max_lag_sources"] - settings["min_lag_sources"] + 1) + n_tests = np.sum( + len(results_comb._single_target[target].sources_tested) + for target in results_comb.targets_analysed + ) * (settings["max_lag_sources"] - settings["min_lag_sources"] + 1) for target in results_comb.targets_analysed: - n_sign = (results_comb._single_target[target]. - selected_sources_pval.size) + if results_comb._single_target[target].selected_sources_pval is None: + continue + n_sign = results_comb._single_target[target].selected_sources_pval.size pval = np.append( - pval, [next_pval if next_pval is not None else 1 for next_pval in - results_comb._single_target[target].selected_sources_pval]) - target_idx = np.append(target_idx, - np.ones(n_sign) * target).astype(int) - cands = (cands + - (results_comb._single_target[target]. - selected_vars_sources)) - n_perm = np.append( - n_perm, results_comb.settings.n_perm_max_seq) + pval, + [ + next_pval if next_pval is not None else 1 + for next_pval in results_comb._single_target[ + target + ].selected_sources_pval + ], + ) + target_idx = np.append(target_idx, np.ones(n_sign) * target).astype(int) + cands = cands + (results_comb._single_target[target].selected_vars_sources) + n_perm = np.append(n_perm, results_comb.settings.n_perm_max_seq) if pval.size == 0: print("No links in final results ...") @@ -208,12 +212,8 @@ def network_fdr(settings=None, *results): # variables is too low, return without performing any correction. if (1 / min(n_perm)) > thresh[0]: print( - "WARNING: Number of permutations (" - "n_perm_max_seq" - ") for at " - "least one target is too low to allow for FDR correction " - "(FDR-threshold: {0:.4f}, min. theoretically possible p-value: " - "{1}).".format(thresh[0], 1 / min(n_perm)) + "WARNING: Number of permutations ('n_perm_max_seq') for at least one target is too low to allow for " + f"FDR correction (FDR-threshold: {thresh[0]:.4f}, min. theoretically possible p-value: {1 / min(n_perm)})." ) results_comb._add_fdr( fdr=None, @@ -243,7 +243,7 @@ def network_fdr(settings=None, *results): t = target_idx[s] cand = cands[s] cand_ind = fdr[t].selected_vars_sources.index(cand) - fdr[t].selected_vars_sources.pop(cand_ind) + fdr[t].selected_vars_sources.remove(cand_ind) fdr[t].selected_sources_pval = np.delete( fdr[t].selected_sources_pval, cand_ind ) @@ -312,7 +312,7 @@ def _perform_fdr_corretion(pval, constant, alpha, n_tests): if np.invert(sign).any(): first_false = np.where(np.invert(sign))[0][0] sign[first_false:] = False # avoids false positives due to equal pvals - sign[sort_idx] = sign.copy() # restore original ordering of significance values + sign[sort_idx] = sign.copy() # restore original ordering of significance values return sign, thresh @@ -347,7 +347,7 @@ def omnibus_test(analysis_setup, data): float the test's p-value float - the estimated test statisic, i.e., the information transfer from + the estimated test statistic, i.e., the information transfer from all sources into the target Raises: From 490a3de85d08b18ac21236b6d0d598ebdd1eac62 Mon Sep 17 00:00:00 2001 From: Patricia Wollstadt Date: Mon, 29 Jan 2024 08:52:57 +0100 Subject: [PATCH 08/34] Refactor removal of non-significant FDR results --- idtxl/stats.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/idtxl/stats.py b/idtxl/stats.py index 1169032..f7b7ec6 100644 --- a/idtxl/stats.py +++ b/idtxl/stats.py @@ -227,10 +227,9 @@ def network_fdr(settings=None, *results): # the results object. Create a copy of the results object to leave the # original intact. fdr = cp.deepcopy(results_comb._single_target) - for s in range(sign.shape[0]): - if not sign[s]: + for i, t in enumerate(target_idx): + if not sign[i]: if correct_by_target: - t = target_idx[s] fdr[t].selected_vars_full = cp.deepcopy( results_comb._single_target[t].selected_vars_target ) @@ -240,17 +239,15 @@ def network_fdr(settings=None, *results): fdr[t].omnibus_pval = 1 fdr[t].omnibus_sign = False else: - t = target_idx[s] - cand = cands[s] - cand_ind = fdr[t].selected_vars_sources.index(cand) - fdr[t].selected_vars_sources.remove(cand_ind) + cand_ind = fdr[t].selected_vars_sources.index(cands[i]) + fdr[t].selected_vars_sources.remove(cands[i]) fdr[t].selected_sources_pval = np.delete( fdr[t].selected_sources_pval, cand_ind ) fdr[t].selected_sources_te = np.delete( fdr[t].selected_sources_te, cand_ind ) - fdr[t].selected_vars_full.pop(fdr[t].selected_vars_full.index(cand)) + fdr[t].selected_vars_full.remove(cands[i]) results_comb._add_fdr(fdr, alpha, correct_by_target, constant) return results_comb From b82945d6bd027006bfd83e1a054ee4478455dbc0 Mon Sep 17 00:00:00 2001 From: Patricia Wollstadt Date: Mon, 29 Jan 2024 08:53:35 +0100 Subject: [PATCH 09/34] Fix future warning for using np.sum on generator --- idtxl/stats.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/idtxl/stats.py b/idtxl/stats.py index f7b7ec6..133df2e 100644 --- a/idtxl/stats.py +++ b/idtxl/stats.py @@ -174,7 +174,7 @@ def network_fdr(settings=None, *results): else: # individual variables # The total number of tests is the number of targets times the number # of source candidates (i.e. source processes * time lags) analyzed for each target - n_tests = np.sum( + n_tests = sum( len(results_comb._single_target[target].sources_tested) for target in results_comb.targets_analysed ) * (settings["max_lag_sources"] - settings["min_lag_sources"] + 1) From 47c93d2d084073f0596690caccf0ada78fe95a5f Mon Sep 17 00:00:00 2001 From: Patricia Wollstadt Date: Mon, 29 Jan 2024 08:55:19 +0100 Subject: [PATCH 10/34] Fix tests for network FDR --- test/test_stats.py | 148 ++++++++++++++++++++++++++++++++------------- 1 file changed, 106 insertions(+), 42 deletions(-) diff --git a/test/test_stats.py b/test/test_stats.py index 6eb51ed..e85d62f 100644 --- a/test/test_stats.py +++ b/test/test_stats.py @@ -1,13 +1,15 @@ """Unit tests for stats module.""" -import pytest +# pylint: disable=protected-access import numpy as np +import pytest +from test_estimators_jidt import _get_gauss_data + from idtxl import stats -from idtxl.multivariate_te import MultivariateTE from idtxl.active_information_storage import ActiveInformationStorage -from idtxl.estimators_jidt import JidtDiscreteCMI from idtxl.data import Data +from idtxl.estimators_jidt import JidtDiscreteCMI +from idtxl.multivariate_te import MultivariateTE from idtxl.results import ResultsNetworkInference, ResultsSingleProcessAnalysis -from test_estimators_jidt import _get_gauss_data def test_omnibus_test(): @@ -53,48 +55,52 @@ def test_max_statistic_sequential(): def test_network_fdr(): - settings = {"n_perm_max_seq": 1000, "n_perm_omnibus": 1000} + # Simulate results for a 3-node network, analyzed all-to-all. Set the + # omnibus p-value for targets 1 and 2 such that they does not survive + # FDR-correction. + settings_fdr = {"n_perm_max_seq": 2000, "n_perm_omnibus": 2000} target_0 = { "selected_vars_sources": [(1, 1), (1, 2), (1, 3), (2, 1), (2, 0)], - "selected_vars_full": [ - (0, 1), - (0, 2), - (0, 3), - (1, 1), - (1, 2), - (1, 3), - (2, 1), - (2, 0), - ], + "selected_vars_target": [(0, 1)], + "sources_tested": [1, 2], "omnibus_pval": 0.0001, "omnibus_sign": True, "selected_sources_pval": np.array([0.001, 0.0014, 0.01, 0.045, 0.047]), "selected_sources_te": np.array([1.1, 1.0, 0.8, 0.7, 0.63]), } + target_0["selected_vars_full"] = ( + target_0["selected_vars_sources"] + target_0["selected_vars_target"] + ) target_1 = { "selected_vars_sources": [(1, 2), (2, 1), (2, 2)], - "selected_vars_full": [(1, 0), (1, 1), (1, 2), (2, 1), (2, 2)], + "selected_vars_target": [(1, 1)], + "sources_tested": [0, 2], "omnibus_pval": 0.031, "omnibus_sign": True, "selected_sources_pval": np.array([0.00001, 0.00014, 0.01]), "selected_sources_te": np.array([1.8, 1.75, 0.75]), } + target_1["selected_vars_full"] = ( + target_1["selected_vars_sources"] + target_1["selected_vars_target"] + ) target_2 = { "selected_vars_sources": [], - "selected_vars_full": [(2, 0), (2, 1)], + "selected_vars_target": [], + "selected_vars_full": [], + "sources_tested": [0, 1], "omnibus_pval": 0.41, "omnibus_sign": False, "selected_sources_pval": None, "selected_sources_te": np.array([]), } res_1 = ResultsNetworkInference(n_nodes=3, n_realisations=1000, normalised=True) - res_1._add_single_result(target=0, settings=settings, results=target_0) - res_1._add_single_result(target=1, settings=settings, results=target_1) + res_1._add_single_result(target=0, settings=settings_fdr, results=target_0) + res_1._add_single_result(target=1, settings=settings_fdr, results=target_1) res_2 = ResultsNetworkInference(n_nodes=3, n_realisations=1000, normalised=True) - res_2._add_single_result(target=2, settings=settings, results=target_2) + res_2._add_single_result(target=2, settings=settings_fdr, results=target_2) for correct_by_target in [True, False]: - settings = { + settings_fdr = { "cmi_estimator": "JidtKraskovCMI", "alpha_fdr": 0.05, "max_lag_sources": 3, @@ -106,37 +112,94 @@ def test_network_fdr(): data.generate_mute_data(n_samples=100, n_replications=3) analysis_setup = MultivariateTE() analysis_setup._initialise( - settings=settings, data=data, sources=[1, 2], target=0 + settings=settings_fdr, data=data, sources=[1, 2], target=0 ) - res_pruned = stats.network_fdr(settings, res_1, res_2) - assert not res_pruned._single_target[ - 2 - ].selected_vars_sources, "Target 2 has not been pruned from results." - - for k in res_pruned.targets_analysed: - if res_pruned._single_target[k]["selected_sources_pval"] is None: - assert not res_pruned._single_target[k]["selected_vars_sources"] - else: + res_pruned = stats.network_fdr(settings_fdr, res_1, res_2) + if correct_by_target: + assert not res_pruned.get_single_target(1, fdr=True)[ + "omnibus_sign" + ], "Target 1 has not been pruned from results." + assert not res_pruned.get_single_target(2, fdr=True)[ + "omnibus_sign" + ], "Target 2 has not been pruned from results." + assert res_pruned.get_single_target(0, fdr=True)[ + "omnibus_sign" + ], "Target 0 has been wrongly pruned from results." + + # Ensure non-sign results were correctly removed after FDR correction. + for t in res_pruned.targets_analysed: + if not res_pruned.get_single_target(t, fdr=True)["omnibus_sign"]: + assert not res_pruned._single_target_fdr[t]["selected_vars_sources"] + assert ( + res_pruned._single_target_fdr[t]["selected_sources_te"] is None + ) + assert ( + res_pruned._single_target_fdr[t]["selected_sources_pval"] + is None + ) + assert res_pruned._single_target_fdr[t]["omnibus_pval"] == 1 + assert not res_pruned._single_target_fdr[t]["omnibus_sign"] + else: + assert len( + res_pruned._single_target[t]["selected_vars_sources"] + ) == len( + res_pruned._single_target[t]["selected_sources_pval"] + ), "Source list and list of p-values should have the same length." + assert len( + res_pruned._single_target[t]["selected_vars_sources"] + ) == len( + res_pruned._single_target_fdr[t]["selected_vars_sources"] + ), "Corrected and uncorrected source list should have the same length." + assert len( + res_pruned._single_target[t]["selected_vars_target"] + ) == len( + res_pruned._single_target_fdr[t]["selected_vars_target"] + ), "Corrected and uncorrected source list should have the same length." + else: + for t in [0, 1]: # ensure correct output for FDR-corrected targets assert len( - res_pruned._single_target[k]["selected_vars_sources"] - ) == len(res_pruned._single_target[k]["selected_sources_pval"]), ( - "Source list and list of p-values should have " "the same length." - ) - + res_pruned.get_single_target(t, fdr=True)["selected_vars_sources"] + ) < len( + res_pruned.get_single_target(t, fdr=False)["selected_vars_sources"] + ), f"Non-sign sources for target {t} have not been pruned from results." + assert len( + res_pruned._single_target_fdr[t]["selected_vars_sources"] + ) == len(res_pruned._single_target_fdr[t]["selected_sources_pval"]) + assert len( + res_pruned._single_target_fdr[t]["selected_vars_sources"] + ) == len(res_pruned._single_target_fdr[t]["selected_sources_te"]) # Test function call for single result - res_pruned = stats.network_fdr(settings, res_1) + res_pruned = stats.network_fdr(settings_fdr, res_1) print("successful call on single result dict.") - # Test None result for insufficient no. permutations, no FDR-corrected - # results (the results class throws an error if no FDR-corrected results - # exist). + # Ensure that no FDR correction is performed if the no. permutations is + # insufficient (the results class throws an error if no FDR-corrected + # results exist). + settings_fdr["correct_by_target"] = True + res_1.settings["n_perm_omnibus"] = 2 + res_2.settings["n_perm_omnibus"] = 2 + res_pruned = stats.network_fdr(settings_fdr, res_1, res_2) + for target in [0, 1, 2]: + with pytest.raises(RuntimeError): + res_pruned.get_single_target(target, fdr=True) + with pytest.raises(RuntimeError): + res_pruned.get_adjacency_matrix("binary", fdr=True) + settings_fdr["correct_by_target"] = False res_1.settings["n_perm_max_seq"] = 2 res_2.settings["n_perm_max_seq"] = 2 - res_pruned = stats.network_fdr(settings, res_1, res_2) + res_pruned = stats.network_fdr(settings_fdr, res_1, res_2) + for target in [0, 1, 2]: + with pytest.raises(RuntimeError): + res_pruned.get_single_target(target, fdr=True) with pytest.raises(RuntimeError): res_pruned.get_adjacency_matrix("binary", fdr=True) +def test_fdr_sorting(): + # Test for correct ordering of p-vals and sign after _perform_fdr_correction + pass + + def test_ais_fdr(): settings = {"n_perm_max_seq": 1000, "n_perm_mi": 1000} process_0 = { @@ -354,10 +417,11 @@ def test_analytical_surrogates(): if __name__ == "__main__": - test_ais_fdr() + # test_ais_fdr() # test_analytical_surrogates() # test_data_type() # test_network_fdr() + test_fdr_sorting() # test_find_pvalue() # test_find_table_max() # test_find_table_min() From af7d90f2bc7db1bdd7124024be1c4b53b9f3312f Mon Sep 17 00:00:00 2001 From: Patricia Wollstadt Date: Mon, 29 Jan 2024 08:57:06 +0100 Subject: [PATCH 11/34] Add log output for seq. max. stats --- idtxl/network_inference.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/idtxl/network_inference.py b/idtxl/network_inference.py index 80b829f..7f888b3 100755 --- a/idtxl/network_inference.py +++ b/idtxl/network_inference.py @@ -1,8 +1,9 @@ """Parent class for all network inference.""" import numpy as np -from .network_analysis import NetworkAnalysis -from . import stats + from . import idtxl_exceptions as ex +from . import stats +from .network_analysis import NetworkAnalysis class NetworkInference(NetworkAnalysis): @@ -235,6 +236,7 @@ def _force_conditionals(self, cond, data): def _remove_non_significant(self, s, p, stat): # Remove non-significant sources from the candidate set. Loop # backwards over the candidates to remove them iteratively. + print(f"removing {np.sum(np.invert(s))} variables after seq. max stats") for i in range(s.shape[0] - 1, -1, -1): if not s[i]: self._remove_selected_var(self.selected_vars_sources[i]) From bba010087245004bc0655f460b066559a7604daa Mon Sep 17 00:00:00 2001 From: Patricia Wollstadt Date: Mon, 29 Jan 2024 09:22:52 +0100 Subject: [PATCH 12/34] Create a copy of p-values in FDR correction By creating a copy, p-values are only sorted within the function and not also in the calling function. In-place sorting does not create a copy, while np.sort() does. --- idtxl/stats.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/idtxl/stats.py b/idtxl/stats.py index 133df2e..e7ad956 100644 --- a/idtxl/stats.py +++ b/idtxl/stats.py @@ -270,11 +270,11 @@ def _perform_fdr_corretion(pval, constant, alpha, n_tests): p-values to be corrected alpha : float critical alpha level - fdr_constant : int + constant : int one of two constants used for calculating the FDR-thresholds according to Genovese (2002): 1 will divide alpha by 1, 2 will divide alpha by the sum_i(1/i); see the paper for details on the - assumptions (default=2) + assumptions n_tests : int total number of tests performed for calculating the FDR-thresholds @@ -286,7 +286,7 @@ def _perform_fdr_corretion(pval, constant, alpha, n_tests): """ # Sort all p-values in ascending order. sort_idx = np.argsort(pval) - pval.sort() + pval_sorted = np.sort(pval) # Calculate threshold if constant == 2: # pick the requested constant (see Genovese, p.872) @@ -302,10 +302,10 @@ def _perform_fdr_corretion(pval, constant, alpha, n_tests): const = 1 # Calculate threshold for each p-value. - thresh = (np.arange(1, len(pval) + 1) / n_tests) * alpha / const + thresh = (np.arange(1, len(pval_sorted) + 1) / n_tests) * alpha / const # Compare data to threshold. - sign = pval <= thresh + sign = pval_sorted <= thresh if np.invert(sign).any(): first_false = np.where(np.invert(sign))[0][0] sign[first_false:] = False # avoids false positives due to equal pvals From 9253d7c86308d76ee07b25c72d14ad354f9f38e6 Mon Sep 17 00:00:00 2001 From: Patricia Wollstadt Date: Mon, 29 Jan 2024 09:25:35 +0100 Subject: [PATCH 13/34] Add test for correct sorting in FDR stats --- test/test_stats.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/test/test_stats.py b/test/test_stats.py index e85d62f..ec70116 100644 --- a/test/test_stats.py +++ b/test/test_stats.py @@ -197,7 +197,20 @@ def test_network_fdr(): def test_fdr_sorting(): # Test for correct ordering of p-vals and sign after _perform_fdr_correction - pass + n_tests = 8 + pval_sorted = np.arange(n_tests) / 300 + constant = 2 + alpha = 0.05 + sign, _ = stats._perform_fdr_corretion(pval_sorted, constant, alpha, n_tests) + assert sign[:3].all() + + permutation = np.random.permutation(n_tests) + pval_unsorted = pval_sorted[permutation].copy() + sign_unsorted, _ = stats._perform_fdr_corretion( + pval_unsorted, constant, alpha, n_tests + ) + assert sign_unsorted.sum() == 3 + assert np.array_equal(pval_sorted[sign], sorted(pval_unsorted[sign_unsorted])) def test_ais_fdr(): @@ -430,4 +443,4 @@ def test_analytical_surrogates(): # test_omnibus_test() # test_max_statistic() # test_min_statistic() - test_max_statistic_sequential() + # test_max_statistic_sequential() From 8abe8a000c166e29ea212c492490655d24ef6dc5 Mon Sep 17 00:00:00 2001 From: Patricia Wollstadt Date: Mon, 29 Jan 2024 14:18:33 +0100 Subject: [PATCH 14/34] Fix formatting of error message --- idtxl/data_spiketime.py | 12 +++++++----- idtxl/estimators_Rudelt.py | 18 ++++++++++-------- test/test_fast_emb.py | 7 +++++-- 3 files changed, 22 insertions(+), 15 deletions(-) diff --git a/idtxl/data_spiketime.py b/idtxl/data_spiketime.py index 2358685..691258c 100644 --- a/idtxl/data_spiketime.py +++ b/idtxl/data_spiketime.py @@ -1,10 +1,12 @@ """Provide spiketime data structures for IDTxl analysis.""" -import numpy as np +import os from sys import stderr + +import numpy as np from scipy.optimize import newton + import idtxl.hde_utils as utl -import os VERBOSE = False @@ -15,9 +17,9 @@ FAST_EMBEDDING_AVAILABLE = False print( """ - Error importing Cython fast embedding module for HDE estimator.\n - When running the HDE estimator, the slow Python implementation for optimizing the HDE embedding will be used,\n - this may take a long time. Other estimators are not affected.\n + Error importing Cython fast embedding module for HDE estimator. + When running the HDE estimator, the slow Python implementation for optimizing the HDE embedding will be used, + this may take a long time. Other estimators are not affected. """, file=stderr, flush=True, diff --git a/idtxl/estimators_Rudelt.py b/idtxl/estimators_Rudelt.py index 69c22c6..f293578 100644 --- a/idtxl/estimators_Rudelt.py +++ b/idtxl/estimators_Rudelt.py @@ -1,14 +1,16 @@ """Provide HDE estimators.""" import logging -import numpy as np -from scipy.optimize import newton, minimize import sys -from sys import stderr -from idtxl.estimator import Estimator -import idtxl.hde_utils as utl from collections import Counter +from sys import stderr + import mpmath as mp +import numpy as np +from scipy.optimize import minimize, newton + +import idtxl.hde_utils as utl +from idtxl.estimator import Estimator FAST_EMBEDDING_AVAILABLE = True try: @@ -17,9 +19,9 @@ FAST_EMBEDDING_AVAILABLE = False print( """ - Error importing Cython fast embedding module for HDE estimator.\n - When running the HDE estimator, the slow Python implementation for optimizing the HDE embedding will be used,\n - this may take a long time. Other estimators are not affected.\n + Error importing Cython fast embedding module for HDE estimator. + When running the HDE estimator, the slow Python implementation for optimizing the HDE embedding will be used, + this may take a long time. Other estimators are not affected. """, file=stderr, flush=True, diff --git a/test/test_fast_emb.py b/test/test_fast_emb.py index 1936273..9c81893 100644 --- a/test/test_fast_emb.py +++ b/test/test_fast_emb.py @@ -13,7 +13,10 @@ try: import idtxl.hde_fast_embedding as fast_emb + print("\nFast embedding tools are available.") except: - print("Error importing Cython fast embedding module. \n Please try the following command in a linux " - "terminal in the IDTxl/idtxl folder: \n>> python3 setup_hde_fast_embedding.py build_ext --inplace") \ No newline at end of file + print( + "Error importing Cython fast embedding module. \n Please try the following command in a linux " + "terminal in the IDTxl/idtxl folder: \n>> python3 setup_hde_fast_embedding.py build_ext --inplace" + ) From fe4956c40da044618c6bd0dfcb7018ff481d16e2 Mon Sep 17 00:00:00 2001 From: Patricia Wollstadt Date: Mon, 29 Jan 2024 14:18:57 +0100 Subject: [PATCH 15/34] Fix formatting and pylint errors --- idtxl/data.py | 37 +++++++++++++++++-------------------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/idtxl/data.py b/idtxl/data.py index 533f6fa..f658e01 100755 --- a/idtxl/data.py +++ b/idtxl/data.py @@ -1,5 +1,6 @@ """Provide data structures for IDTxl analysis.""" import numpy as np + from . import idtxl_utils as utils VERBOSE = False @@ -145,7 +146,7 @@ def set_data(self, data, dim_order): must have the same length as number of dimensions in data """ if len(dim_order) > 3: - raise RuntimeError("dim_order can not have more than three " "entries") + raise RuntimeError("dim_order can not have more than three entries") if len(dim_order) != data.ndim: raise RuntimeError( f"Data array dimension ({data.ndim}) and length of " @@ -157,8 +158,8 @@ def set_data(self, data, dim_order): data_ordered = self._reorder_data(data, dim_order) self._set_data_size(data_ordered) print( - "Adding data with properties: {0} processes, {1} samples, {2} " - "replications".format(self.n_processes, self.n_samples, self.n_replications) + f"Adding data with properties: {self.n_processes} processes, {self.n_samples} " + f"samples, {self.n_replications} replications" ) try: delattr(self, "data") @@ -256,7 +257,7 @@ def get_realisations(self, current_value, idx_list, shuffle=False): samples * no.replications) x number of indices """ if not hasattr(self, "data"): - raise AttributeError("No data has been added to this Data() " "instance.") + raise AttributeError("No data has been added to this Data() instance.") # Return None if index list is empty. if not idx_list: return None, None @@ -296,12 +297,11 @@ def get_realisations(self, current_value, idx_list, shuffle=False): realisations[r : r + n_real_time, i] = self.data[ idx[0], idx[1] : last_sample, replication ] - except IndexError: + except IndexError as e: raise IndexError( - "You tried to access variable {0} in a " - "data set with {1} processes and {2} " - "samples.".format(idx, self.n_processes, self.n_samples) - ) + f"You tried to access variable {idx} in a data set with {self.n_processes} " + f"processes and {self.n_samples} samples." + ) from e r += n_real_time assert not np.isnan( @@ -378,14 +378,11 @@ def _get_data_slice(self, process, offset_samples=0, shuffle=False): try: data_slice = self.data[process, offset_samples:, replication_index] - except IndexError: + except IndexError as e: raise IndexError( - "You tried to access process {0} with an offset " - "of {1} in a data set of {2} processes and {3} " - "samples.".format( - process, offset_samples, self.n_processes, self.n_samples - ) - ) + "You tried to access process {process} with an offset of {offset_samples} in a data set " + "of {self.n_processes} processes and {self.n_samples} samples." + ) from e assert not np.isnan( data_slice ).any(), "There are nans in the retrieved data slice." @@ -706,22 +703,22 @@ def _get_permutation_samples(self, n_samples, perm_settings): elif perm_type == "circular": max_shift = perm_settings["max_shift"] if not isinstance(max_shift, int) or max_shift < 1: - raise TypeError(" " "max_shift" " has to be an int > 0.") + raise TypeError("'max_shift' has to be an int > 0.") perm = self._circular_shift(n_samples, max_shift)[0] elif perm_type == "block": block_size = perm_settings["block_size"] perm_range = perm_settings["perm_range"] if not isinstance(block_size, int) or block_size < 1: - raise TypeError(" " "block_size" " has to be an int > 0.") + raise TypeError("'block_size' has to be an int > 0.") if not isinstance(perm_range, int) or perm_range < 1: - raise TypeError(" " "perm_range" " has to be an int > 0.") + raise TypeError("'perm_range' has to be an int > 0.") perm = self._swap_blocks(n_samples, block_size, perm_range) elif perm_type == "local": perm_range = perm_settings["perm_range"] if not isinstance(perm_range, int) or perm_range < 1: - raise TypeError(" " "perm_range" " has to be an int > 0.") + raise TypeError("'perm_range' has to be an int > 0.") perm = self._swap_local(n_samples, perm_range) else: From 312f587ae83f7c5a60ec3ce22ae2041d8e2acf48 Mon Sep 17 00:00:00 2001 From: Patricia Wollstadt Date: Mon, 29 Jan 2024 14:19:26 +0100 Subject: [PATCH 16/34] Fix unsorting and surrogate calculation in bivariate sequential max stats Fix unsorting of values per source, and unsorting of variables across source processes. Fix calculation of surrogate values per variable. For bivariate sequential max stats, variables are tested per source process. When returning TE values, p-values, and significance information, take care to sort values in the same order as tested source variables in the analysis object. The surrogate statistics were not correctly calculated for every candidate. Fix formatting --- idtxl/stats.py | 221 ++++++++++++++++++++----------------------------- 1 file changed, 89 insertions(+), 132 deletions(-) diff --git a/idtxl/stats.py b/idtxl/stats.py index e7ad956..9d26a71 100644 --- a/idtxl/stats.py +++ b/idtxl/stats.py @@ -547,8 +547,7 @@ def max_statistic_sequential(analysis_setup, data): if analysis_setup.settings["verbose"]: print( - "sequential maximum statistic, n_perm: {0}, testing {1} selected" - " sources".format(n_permutations, len(analysis_setup.selected_vars_sources)) + f"sequential maximum statistic, n_perm: {n_permutations}, testing {len(analysis_setup.selected_vars_sources)} selected sources" ) assert analysis_setup.selected_vars_sources, "No sources to test." @@ -637,11 +636,9 @@ def max_statistic_sequential(analysis_setup, data): # we'll terminate the max sequential stats test, # and declare all not significant print( - "AlgorithmExhaustedError encountered in estimations: {}.".format( - aee.message - ) + f"AlgorithmExhaustedError encountered in estimations: {aee.message}. " + "Stopping sequential max stats at candidate with rank 0" ) - print("Stopping sequential max stats at candidate with rank 0") return ( np.zeros(len(analysis_setup.selected_vars_sources)).astype(bool), np.ones(len(analysis_setup.selected_vars_sources)), @@ -662,11 +659,9 @@ def max_statistic_sequential(analysis_setup, data): # we'll terminate the max sequential stats test, # and declare all not significant print( - "AlgorithmExhaustedError encountered in estimations: {}.".format( - aee.message - ) + f"AlgorithmExhaustedError encountered in estimations: {aee.message}. " + "Stopping sequential max stats at candidate with rank 0" ) - print("Stopping sequential max stats at candidate with rank 0") # For now we don't need a stack trace: # traceback.print_tb(aee.__traceback__) # Return (signficance, pvalue, TEs): @@ -692,10 +687,7 @@ def max_statistic_sequential(analysis_setup, data): pvalue[c] = p if not s: # break as soon as a candidate is no longer significant if analysis_setup.settings["verbose"]: - print( - "\nStopping sequential max stats at candidate with rank " - "{0}.".format(c) - ) + print(f"\nStopping sequential max stats at candidate with rank {c}.") break # Get back original order and return results. @@ -765,8 +757,7 @@ def max_statistic_sequential_bivariate(analysis_setup, data): if analysis_setup.settings["verbose"]: print( - "sequential maximum statistic, n_perm: {0}, testing {1} selected" - " sources".format(n_permutations, len(analysis_setup.selected_vars_sources)) + f"sequential maximum statistic, n_perm: {n_permutations}, testing {len(analysis_setup.selected_vars_sources)} selected sources" ) assert analysis_setup.selected_vars_sources, "No sources to test." @@ -788,12 +779,17 @@ def max_statistic_sequential_bivariate(analysis_setup, data): ) significance = np.zeros(len(analysis_setup.selected_vars_sources)).astype(bool) pvalue = np.ones(len(analysis_setup.selected_vars_sources)) - stat = np.zeros(len(analysis_setup.selected_vars_sources)) + individual_stat = np.zeros(len(analysis_setup.selected_vars_sources)) for source in significant_sources: # Find selected past variables for current source source_vars = [ s for s in analysis_setup.selected_vars_sources if s[0] == source ] + source_vars_idx = [ + i + for i, s in enumerate(analysis_setup.selected_vars_sources) + if s[0] == source + ] # Determine length of conditioning set and allocate memory. idx_conditional = source_vars.copy() @@ -825,8 +821,8 @@ def max_statistic_sequential_bivariate(analysis_setup, data): temp_cand = data.get_realisations( analysis_setup.current_value, [candidate] )[0] - # The following may happen if either the requested conditing is - # 'none' or if the conditiong set that is tested consists only of + # The following may happen if either the requested conditioning is + # 'none' or if the conditioning set that is tested consists only of # a single candidate. if temp_cond is None: conditional_realisations = conditional_realisations_target @@ -843,58 +839,60 @@ def max_statistic_sequential_bivariate(analysis_setup, data): i_1 = i_2 i_2 += data.n_realisations(analysis_setup.current_value) - # Generate surrogates for the current candidate. - if ( - analysis_setup._cmi_estimator.is_analytic_null_estimator() - and permute_in_time - ): - # Generate the surrogates analytically - surr_table[ - idx_c, : - ] = analysis_setup._cmi_estimator.estimate_surrogates_analytic( - n_perm=n_permutations, - var1=data.get_realisations(analysis_setup.current_value, [candidate])[ - 0 - ], - var2=analysis_setup._current_value_realisations, - conditional=temp_cond, - ) - else: - analysis_setup.settings["analytical_surrogates"] = False - surr_candidate_realisations = _get_surrogates( - data, - analysis_setup.current_value, - [candidate], - n_permutations, - analysis_setup.settings, - ) - try: - surr_table[idx_c, :] = analysis_setup._cmi_estimator.estimate_parallel( - n_chunks=n_permutations, - re_use=["var2", "conditional"], - var1=surr_candidate_realisations, + # Generate surrogates for the current candidate. + if ( + analysis_setup._cmi_estimator.is_analytic_null_estimator() + and permute_in_time + ): + # Generate the surrogates analytically + surr_table[ + idx_c, : + ] = analysis_setup._cmi_estimator.estimate_surrogates_analytic( + n_perm=n_permutations, + var1=data.get_realisations( + analysis_setup.current_value, [candidate] + )[0], var2=analysis_setup._current_value_realisations, conditional=temp_cond, ) - except ex.AlgorithmExhaustedError as aee: - # The aglorithm cannot continue here, so - # we'll terminate the max sequential stats test, - # and declare all not significant - print( - "AlgorithmExhaustedError encountered in estimations: {}.".format( - aee.message - ) - ) - print("Stopping sequential max stats at candidate with rank 0") - return ( - np.zeros(len(analysis_setup.selected_vars_sources)).astype(bool), - np.ones(len(analysis_setup.selected_vars_sources)), - np.zeros(len(analysis_setup.selected_vars_sources)), + else: + analysis_setup.settings["analytical_surrogates"] = False + surr_candidate_realisations = _get_surrogates( + data, + analysis_setup.current_value, + [candidate], + n_permutations, + analysis_setup.settings, ) + try: + surr_table[ + idx_c, : + ] = analysis_setup._cmi_estimator.estimate_parallel( + n_chunks=n_permutations, + re_use=["var2", "conditional"], + var1=surr_candidate_realisations, + var2=analysis_setup._current_value_realisations, + conditional=temp_cond, + ) + except ex.AlgorithmExhaustedError as aee: + # The aglorithm cannot continue here, so + # we'll terminate the max sequential stats test, + # and declare all not significant + print( + f"AlgorithmExhaustedError encountered in estimations: {aee.message}. " + "Stopping sequential max stats at candidate with rank 0" + ) + return ( + np.zeros(len(analysis_setup.selected_vars_sources)).astype( + bool + ), + np.ones(len(analysis_setup.selected_vars_sources)), + np.zeros(len(analysis_setup.selected_vars_sources)), + ) - # Calculate original statistic (multivariate/bivariate TE/MI) + # Calculate original statistic (bivariate TE/MI) try: - individual_stat = analysis_setup._cmi_estimator.estimate_parallel( + individual_stat_source = analysis_setup._cmi_estimator.estimate_parallel( n_chunks=len(source_vars), re_use=re_use, var1=candidate_realisations, @@ -906,10 +904,9 @@ def max_statistic_sequential_bivariate(analysis_setup, data): # we'll terminate the max sequential stats test, # and declare all not significant print( - "AlgorithmExhaustedError encountered in " - "estimations: {}.".format(aee.message) + f"AlgorithmExhaustedError encountered in estimations: {aee.message}. " + "Stopping sequential max stats at candidate with rank 0" ) - print("Stopping sequential max stats at candidate with rank 0") # For now we don't need a stack trace: # traceback.print_tb(aee.__traceback__) # Return (signficance, pvalue, TEs): @@ -919,38 +916,40 @@ def max_statistic_sequential_bivariate(analysis_setup, data): np.zeros(len(analysis_setup.selected_vars_sources)), ) - selected_vars_order = utils.argsort_descending(individual_stat) - individual_stat_sorted = utils.sort_descending(individual_stat) + selected_vars_order = utils.argsort_descending(individual_stat_source) + individual_stat_source_sorted = utils.sort_descending(individual_stat_source) max_distribution = _sort_table_max(surr_table) + significance_source = np.zeros(individual_stat_source.shape[0], dtype=bool) + pvalue_source = np.ones(individual_stat_source.shape[0]) # Compare each original value with the distribution of the same rank, # starting with the highest value. - for c in range(individual_stat.shape[0]): - [s, p] = _find_pvalue( - individual_stat_sorted[c], + for c in range(individual_stat_source.shape[0]): + s, p = _find_pvalue( + individual_stat_source_sorted[c], max_distribution[c,], alpha, tail="one_bigger", ) - # Write results into an array with the same order as the set of - # selected sources from all process. Find the currently tested - # variable and its index in the list of all selected variables. - current_var = source_vars[selected_vars_order[c]] - for ind, v in enumerate(analysis_setup.selected_vars_sources): - if v == current_var: - break - significance[ind] = s - pvalue[ind] = p - stat[ind] = individual_stat_sorted[c] + significance_source[c] = s + pvalue_source[c] = p + if not s: # break as soon as a candidate is no longer significant if analysis_setup.settings["verbose"]: print( - "\nStopping sequential max stats at candidate with " - "rank {0}.".format(c) + f"\nStopping sequential max stats at candidate with rank {c}." ) break - return significance, pvalue, stat + # Get back original order of variables within the current source. Write + # re-ordered results into global results array at the respective + # variable locations. + significance_source[selected_vars_order] = significance_source.copy() + pvalue_source[selected_vars_order] = pvalue_source.copy() + significance[source_vars_idx] = significance_source + pvalue[source_vars_idx] = pvalue_source + individual_stat[source_vars_idx] = individual_stat_source + return significance, pvalue, individual_stat def min_statistic( @@ -1069,38 +1068,9 @@ def mi_against_surrogates(analysis_setup, data): permute_in_time = _check_permute_in_time(analysis_setup, data, n_perm) if analysis_setup.settings["verbose"]: print( - "mi permutation test against surrogates, n_perm: {0}".format( - analysis_setup.settings["n_perm_mi"] - ) + f"mi permutation test against surrogates, n_perm: {analysis_setup.settings['n_perm_mi']}" ) - """ - surr_realisations = np.empty( - (data.n_realisations(analysis_setup.current_value) * - (n_perm + 1), 1)) - i_1 = 0 - i_2 = data.n_realisations(analysis_setup.current_value) - # The first chunk holds the original data - surr_realisations[i_1:i_2, ] = analysis_setup._current_value_realisations - # Create surrogate data by shuffling the realisations of the current value. - for perm in range(n_perm): - i_1 = i_2 - i_2 += data.n_realisations(analysis_setup.current_value) - # Check the permutation type for the current candidate. - if permute_over_replications: - surr_temp = data.permute_data(analysis_setup.current_value, - [analysis_setup.current_value])[0] - else: - [real, repl_idx] = data.get_realisations( - analysis_setup.current_value, - [analysis_setup.current_value]) - surr_temp = _permute_realisations(real, repl_idx, perm_range) - # Add current shuffled realisation to the array of all realisations for - # parallel MI estimation. - # surr_realisations[i_1:i_2, ] = surr_temp - [real, repl_idx] = data.get_realisations( - analysis_setup.current_value, - [analysis_setup.current_value]) - """ + if analysis_setup._cmi_estimator.is_analytic_null_estimator() and permute_in_time: # Generate the surrogates analytically analysis_setup.settings["analytical_surrogates"] = True @@ -1524,7 +1494,7 @@ def _find_pvalue(statistic, distribution, alpha, tail): assert distribution.ndim == 1, "Test distribution must be 1D." check_n_perm(distribution.shape[0], alpha) - if tail == "one_bigger" or tail == "one": + if tail in ["one_bigger", "one"]: pvalue = sum(distribution >= statistic) / distribution.shape[0] elif tail == "one_smaller": pvalue = sum(distribution <= statistic) / distribution.shape[0] @@ -1535,20 +1505,7 @@ def _find_pvalue(statistic, distribution, alpha, tail): alpha = alpha / 2 else: raise ValueError( - ( - "Unkown value for " - "tail" - ", should be " - "one" - ", " - "one_bigger" - "," - " " - "one_smaller" - ", or " - "two" - "): {0}.".format(tail) - ) + f"Unkown value for tail: {tail}, should be one, one_bigger, one_smaller, or two" ) # If the statistic is larger than all values in the test distribution, set From b18403b7ae83a3a3414d739750340fb1e2194612 Mon Sep 17 00:00:00 2001 From: Patricia Wollstadt Date: Mon, 29 Jan 2024 14:24:43 +0100 Subject: [PATCH 17/34] Add unit tests for sequential max. stats --- test/test_stats.py | 89 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 77 insertions(+), 12 deletions(-) diff --git a/test/test_stats.py b/test/test_stats.py index ec70116..872cd65 100644 --- a/test/test_stats.py +++ b/test/test_stats.py @@ -25,6 +25,7 @@ def test_min_statistic(): def test_max_statistic_sequential(): + np.random.seed(0) data = Data() data.generate_mute_data(104, 10) settings = { @@ -38,20 +39,82 @@ def test_max_statistic_sequential(): "max_lag_target": 5, } setup = MultivariateTE() - setup._initialise(settings, data, sources=[0, 1], target=2) - setup.current_value = (0, 4) - setup.selected_vars_sources = [(1, 1), (1, 2)] - setup.selected_vars_full = [(0, 1), (1, 1), (1, 2)] - setup._selected_vars_realisations = np.random.rand( - data.n_realisations(setup.current_value), len(setup.selected_vars_full) - ) - setup._current_value_realisations = np.random.rand( - data.n_realisations(setup.current_value), 1 + setup._initialise(settings, data, sources=[0, 2], target=1) + setup.current_value = (1, 4) + setup.selected_vars_target = [(1, 1)] + setup.selected_vars_sources = [(0, 1), (2, 1)] + setup.selected_vars_full = setup.selected_vars_target + setup.selected_vars_sources + + setup._current_value_realisations = data.get_realisations( + setup.current_value, [setup.current_value] + )[0] + + setup._selected_vars_realisations = np.hstack( + ( + data.get_realisations( # use actual realisation for target var and first source var + setup.current_value, + [setup.selected_vars_target[0], setup.selected_vars_sources[0]], + )[ + 0 + ], + np.random.rand( + data.n_realisations(setup.current_value), 1 + ), # use random data as realizations for second source var + ) ) [sign, p, te] = stats.max_statistic_sequential(analysis_setup=setup, data=data) + print(p) + print(sign) + print(te) + assert len(p) == len(te) + assert len(p) == len(sign) + assert len(p) == len(setup.selected_vars_sources) + + setup = MultivariateTE() + setup._initialise(settings, data, sources=[0, 1, 4], target=3) + setup.current_value = (3, 4) + setup.selected_vars_target = [(1, 1)] + setup.selected_vars_sources = [(0, 1), (0, 2), (1, 1), (4, 1)] + setup.selected_vars_full = setup.selected_vars_target + setup.selected_vars_sources + setup._current_value_realisations = data.get_realisations( + setup.current_value, [setup.current_value] + )[0] + setup._selected_vars_realisations = data.get_realisations( + setup.current_value, setup.selected_vars_full + )[0] + [sign, p, te] = stats.max_statistic_sequential_bivariate( analysis_setup=setup, data=data ) + assert len(p) == len(te) + assert len(p) == len(sign) + assert len(p) == len(setup.selected_vars_sources) + print(p) + print(setup.selected_vars_sources) + print(sign) + print(te, "\n\n") + + for t in [0, 4]: + np.random.seed(0) + data.generate_mute_data(104, 10) + data._data[t, :, :] = np.random.rand(104, 10) + setup._current_value_realisations = data.get_realisations( + setup.current_value, [setup.current_value] + )[0] + setup._selected_vars_realisations = data.get_realisations( + setup.current_value, setup.selected_vars_full + )[0] + [sign, p, te] = stats.max_statistic_sequential_bivariate( + analysis_setup=setup, data=data + ) + for i, var in enumerate(setup.selected_vars_sources): + if var[0] == t: + assert not sign[ + i + ], "Seq. max. stats returned sign result for random data" + assert ( + p[i] > setup.settings["alpha_max_seq"] + ), f"{p[i]} not smaller than critical alpha {setup.settings['alpha_max_seq']}" def test_network_fdr(): @@ -407,7 +470,9 @@ def test_analytical_surrogates(): expected_mi, source1, source2, target = _get_gauss_data(covariance=0.4) settings = {"discretise_method": "equal", "n_discrete_bins": 5} est = JidtDiscreteCMI(settings) - source_dis, target_dis = est._discretise_vars(var1=source1, var2=target) + source_dis, target_dis = est._discretise_vars( # pylint: disable=W0632 + var1=source1, var2=target + ) data = Data(np.hstack((source_dis, target_dis)), dim_order="sp", normalise=False) settings = { "cmi_estimator": "JidtDiscreteCMI", @@ -434,7 +499,7 @@ def test_analytical_surrogates(): # test_analytical_surrogates() # test_data_type() # test_network_fdr() - test_fdr_sorting() + # test_fdr_sorting() # test_find_pvalue() # test_find_table_max() # test_find_table_min() @@ -443,4 +508,4 @@ def test_analytical_surrogates(): # test_omnibus_test() # test_max_statistic() # test_min_statistic() - # test_max_statistic_sequential() + test_max_statistic_sequential() From 935161bee4263ce48400f673d007906fbd14beb1 Mon Sep 17 00:00:00 2001 From: Patricia Wollstadt Date: Mon, 29 Jan 2024 14:27:09 +0100 Subject: [PATCH 18/34] Fix error in input check --- idtxl/multivariate_te.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/idtxl/multivariate_te.py b/idtxl/multivariate_te.py index b727b3f..8464344 100755 --- a/idtxl/multivariate_te.py +++ b/idtxl/multivariate_te.py @@ -135,7 +135,7 @@ def analyse_network(self, settings, data, targets="all", sources="all"): targets = list(range(data.n_processes)) if sources == "all": sources = ["all" for t in targets] - elif isinstance(sources is list) and isinstance(sources[0], int): + elif isinstance(sources, list) and isinstance(sources[0], int): sources = [sources for t in targets] elif isinstance(sources, list) and isinstance(sources[0], list): pass @@ -154,12 +154,10 @@ def analyse_network(self, settings, data, targets="all", sources="all"): n_realisations=data.n_realisations(), normalised=data.normalise, ) - for t in range(len(targets)): + for t, target in enumerate(targets): if settings["verbose"]: print(f"\n####### analysing target with index {t} from list {targets}") - res_single = self.analyse_single_target( - settings, data, targets[t], sources[t] - ) + res_single = self.analyse_single_target(settings, data, target, sources[t]) results.combine_results(res_single) # Get no. realisations actually used for estimation from single target From b11c69c121e1d623eed1552a8fbb0371e538b296 Mon Sep 17 00:00:00 2001 From: Patricia Wollstadt Date: Mon, 29 Jan 2024 14:41:26 +0100 Subject: [PATCH 19/34] Fix docstrings --- idtxl/estimators_multivariate_pid.py | 61 +++++++++++++++------------- idtxl/idtxl_utils.py | 23 +++++------ idtxl/network_analysis.py | 16 ++++---- idtxl/results.py | 35 ++++++++-------- idtxl/stats.py | 10 ++--- 5 files changed, 74 insertions(+), 71 deletions(-) diff --git a/idtxl/estimators_multivariate_pid.py b/idtxl/estimators_multivariate_pid.py index ad40463..46432f1 100755 --- a/idtxl/estimators_multivariate_pid.py +++ b/idtxl/estimators_multivariate_pid.py @@ -8,6 +8,7 @@ http://arxiv.org/abs/2002.03356 """ import numpy as np + from . import lattices as lt from . import pid_goettingen from .estimator import Estimator @@ -47,7 +48,7 @@ class SxPID(Estimator): def __init__(self, settings): # get estimation parameters self.settings = settings.copy() - self.settings.setdefault('verbose', False) + self.settings.setdefault("verbose", False) def is_parallel(): return False @@ -56,7 +57,8 @@ def is_analytic_null_estimator(self): return False def estimate(self, s, t): - """ + """Estimate SxPID from list of sources and a target + Args: s : list of numpy arrays 1D arrays containing realizations of a discrete random variable @@ -64,16 +66,12 @@ def estimate(self, s, t): 1D array containing realizations of a discrete random variable Returns: - dict of dict - { - 'ptw' -> { realization -> {alpha -> [float, float, float]} } - - 'avg' -> {alpha -> [float, float, float]} - } - where the list of floats is ordered - [informative, misinformative, informative - misinformative] - ptw stands for pointwise decomposition - avg stands for average decomposition + dict + SxPID results, with entries + - 'ptw' -> { realization -> {alpha -> [float, float, float]}}: pointwise decomposition + - 'avg' -> {alpha -> [float, float, float]}: average decomposition + + the list of floats is ordered [informative, misinformative, informative - misinformative] """ s, t, self.settings = _check_input(s, t, self.settings) pdf = _get_pdf_dict(s, t) @@ -90,19 +88,20 @@ def estimate(self, s, t): pdf_orig=pdf, chld=lattices[num_source_vars][0], achain=lattices[num_source_vars][1], - printing=self.settings['verbose']) + printing=self.settings["verbose"], + ) # TODO AskM: Trivariate: does it make sense to name the alphas # for example shared_syn_s1_s2__syn_s1_s3 ? results = { - 'ptw': retval_ptw, - 'avg': retval_avg, + "ptw": retval_ptw, + "avg": retval_avg, } return results def _get_pdf_dict(s, t): - """"Write probability mass function estimated via counting to a dict.""" + """ "Write probability mass function estimated via counting to a dict.""" # Create dictionary with probability mass function counts = dict() n_samples = s[0].shape[0] @@ -126,10 +125,10 @@ def _check_input(s, t, settings): """Check input to PID estimators.""" # Check if inputs are numpy arrays. if type(t) != np.ndarray: - raise TypeError('Input t must be a numpy array.') + raise TypeError("Input t must be a numpy array.") for i in range(len(s)): if type(s[i]) != np.ndarray: - raise TypeError('All inputs s{0} must be numpy arrays.'.format(i+1)) + raise TypeError("All inputs s{0} must be numpy arrays.".format(i + 1)) # In general, IDTxl expects 2D inputs because JIDT/JPYPE only accepts those # and we have a multivariate approach, i.e., a vector is a special case of @@ -146,33 +145,37 @@ def _check_input(s, t, settings): for col in range(1, s[i].shape[1]): alph_col = len(np.unique(s[i][:, col])) si_joint, alph_new = _join_variables( - si_joint, s[i][:, col], alph_new, alph_col) - settings['alph_s'+str(i+1)] = alph_new + si_joint, s[i][:, col], alph_new, alph_col + ) + settings["alph_s" + str(i + 1)] = alph_new else: - raise ValueError('Input source {0} s{0} has to be a 1D or 2D ' - 'numpy array.'.format(i+1)) + raise ValueError( + "Input source {0} s{0} has to be a 1D or 2D " + "numpy array.".format(i + 1) + ) if t.ndim != 1: if t.shape[1] == 1: t = np.squeeze(t) else: # For now we only allow 1D-targets - raise ValueError('Input target t has to be a vector ' - '(t.shape[1]=1).') + raise ValueError("Input target t has to be a vector " "(t.shape[1]=1).") # Check types of remaining inputs. if type(settings) != dict: - raise TypeError('The settings argument should be a dictionary.') + raise TypeError("The settings argument should be a dictionary.") for i in range(len(s)): if not issubclass(s[i].dtype.type, np.integer): - raise TypeError('Input s{0} (source {0}) must be an integer numpy ' - 'array.'.format(i+1)) + raise TypeError( + "Input s{0} (source {0}) must be an integer numpy " + "array.".format(i + 1) + ) # ^ for if not issubclass(t.dtype.type, np.integer): - raise TypeError('Input t (target) must be an integer numpy array.') + raise TypeError("Input t (target) must be an integer numpy array.") # Check if variables have equal length. for i in range(len(s)): if len(t) != len(s[i]): - raise ValueError('Number of samples s and t must be equal') + raise ValueError("Number of samples s and t must be equal") return s, t, settings diff --git a/idtxl/idtxl_utils.py b/idtxl/idtxl_utils.py index 50c85d9..f6ce0e4 100755 --- a/idtxl/idtxl_utils.py +++ b/idtxl/idtxl_utils.py @@ -1,9 +1,10 @@ """Provide IDTxl utility functions.""" -import pprint import copy as cp -import numpy as np +import pprint import threading +import numpy as np + def swap_chars(s, i_1, i_2): """Swap to characters in a string. @@ -316,14 +317,7 @@ def conflicting_entries(dict_1, dict_2): for k in intersect_keys: if np.array(dict_1[k] != dict_2[k]).any(): print( - "Unequal entries for key " - "{0}" - ": dict_1: " - "{1}" - ", dict_2:" - " " - "{2}" - ".".format(k, dict_1[k], dict_2[k]) + f"Unequal entries for key {k}: dict_1: {dict_1[k]}, dict_2: {dict_2[k]}" ) return True return False @@ -336,9 +330,12 @@ def calculate_mi(corr): class timeout(object): """Context manager for a timeout using threading module. - args: - timeout_duration: float, number of seconds to wait before timeout is triggered - exception_message: string, message to put in the exception + + Args: + timeout_duration: float + number of seconds to wait before timeout is triggered + exception_message : string + message to put in the exception """ def __init__(self, timeout_duration, exception_message="Timeout"): diff --git a/idtxl/network_analysis.py b/idtxl/network_analysis.py index d312674..7e9adde 100644 --- a/idtxl/network_analysis.py +++ b/idtxl/network_analysis.py @@ -1,16 +1,18 @@ """Parent class for network inference and network comparison. """ -import os.path -from datetime import datetime -from shutil import copyfile -from pprint import pprint import ast import copy as cp import itertools as it +import os.path +from datetime import datetime +from pprint import pprint +from shutil import copyfile + import numpy as np -from .estimator import get_estimator -from . import idtxl_utils as utils + from . import idtxl_io as io +from . import idtxl_utils as utils +from .estimator import get_estimator class NetworkAnalysis: @@ -662,7 +664,7 @@ def resume_checkpoint(self, file_path): Args: file_path : str - path to checkpoint file (excluding extension: *.ckp) + path to checkpoint file (excluding extension: .ckp) """ # Read checkpoint diff --git a/idtxl/results.py b/idtxl/results.py index 84f3702..ce33470 100644 --- a/idtxl/results.py +++ b/idtxl/results.py @@ -1,8 +1,10 @@ """Provide results class for IDTxl network analysis.""" +import copy as cp import sys import warnings -import copy as cp + import numpy as np + from . import idtxl_utils as utils warnings.simplefilter(action="ignore", category=FutureWarning) @@ -1158,38 +1160,39 @@ def get_single_process(self, process): (result['selected_vars']) or via dot-notation (result.selected_vars). Contains keys - Process : int + - Process : int Process that was optimized - estimation_method : String + - estimation_method : String Estimation method that was used for optimization - T_D : float + - T_D : float Estimated optimal value for the temporal depth TD - tau_R : + - tau_R : Information timescale tau_R, a characteristic timescale of history dependence similar to an autocorrelation time. - R_tot : float + - R_tot : float Estimated value for the total history dependence Rtot, - AIS_tot : float + - AIS_tot : float Estimated value for the total active information storage - opt_number_of_bins_d : int + - opt_number_of_bins_d : int Number of bins d for the embedding that yields (R̂tot ,T̂D) - opt_scaling_k : int + - opt_scaling_k : int Scaling exponent κ for the embedding that yields (R̂tot , T̂D) - opt_first_bin_size : int + - opt_first_bin_size : int Size of the first bin τ1 for the embedding that yields (R̂tot , T̂D ), - history_dependence : array with floating-point values + - history_dependence : array with floating-point values Estimated history dependence for each embedding - firing_rate : float + - firing_rate : float Firing rate of the neuron/ spike train - recording_length : float + - recording_length : float Length of the recording (in seconds) - H_spiking : float + - H_spiking : float Entropy of the spike times if analyse_auto_MI was set to True additionally: - auto_MI : dict + + - auto_MI : dict numpy array of MI values for each delay - auto_MI_delays : list of int + - auto_MI_delays : list of int list of delays depending on the given auto_MI_bin_sizes and auto_MI_max_delay """ diff --git a/idtxl/stats.py b/idtxl/stats.py index 9d26a71..7ee3852 100644 --- a/idtxl/stats.py +++ b/idtxl/stats.py @@ -513,13 +513,12 @@ def max_statistic_sequential(analysis_setup, data): calculation of surrogates for this statistic. Args: - analysis_setup : MultivariateTE instance information on the current analysis, can have an optional attribute - 'settings', a dictionary with parameters for statistical testing: + settings, a dictionary with parameters for statistical testing: - n_perm_max_seq : int [optional] - number of permutations - (default='n_perm_min_stat'|500) + (default=n_perm_min_stat|500) - alpha_max_seq : float [optional] - critical alpha level (default=0.05) - permute_in_time : bool [optional] - generate surrogates by @@ -723,13 +722,12 @@ def max_statistic_sequential_bivariate(analysis_setup, data): calculation of surrogates for this statistic. Args: - analysis_setup : MultivariateTE instance information on the current analysis, can have an optional attribute - 'settings', a dictionary with parameters for statistical testing: + settings, a dictionary with parameters for statistical testing: - n_perm_max_seq : int [optional] - number of permutations - (default='n_perm_min_stat'|500) + (default=n_perm_min_stat|500) - alpha_max_seq : float [optional] - critical alpha level (default=0.05) - permute_in_time : bool [optional] - generate surrogates by From 4c6228ba48829eef6b84c429446ca8c8bdcb5889 Mon Sep 17 00:00:00 2001 From: Patricia Wollstadt Date: Mon, 29 Jan 2024 14:41:41 +0100 Subject: [PATCH 20/34] Update sphinx documentation --- docs/doctrees/environment.pickle | Bin 921680 -> 920870 bytes docs/doctrees/idtxl.doctree | Bin 1843274 -> 1845713 bytes docs/doctrees/idtxl_helper.doctree | Bin 163080 -> 164078 bytes docs/doctrees/idtxl_results_class.doctree | Bin 149071 -> 151963 bytes docs/html/.buildinfo | 2 +- .../idtxl/active_information_storage.html | 6 +- docs/html/_modules/idtxl/bivariate_mi.html | 6 +- docs/html/_modules/idtxl/bivariate_pid.html | 6 +- docs/html/_modules/idtxl/bivariate_te.html | 6 +- docs/html/_modules/idtxl/data.html | 43 +-- .../embedding_optimization_ais_Rudelt.html | 6 +- .../_modules/idtxl/estimators_Rudelt.html | 24 +- docs/html/_modules/idtxl/estimators_jidt.html | 6 +- docs/html/_modules/idtxl/estimators_mpi.html | 6 +- .../idtxl/estimators_multivariate_pid.html | 67 ++-- .../_modules/idtxl/estimators_opencl.html | 6 +- docs/html/_modules/idtxl/estimators_pid.html | 6 +- .../_modules/idtxl/estimators_python.html | 6 +- .../html/_modules/idtxl/idtxl_exceptions.html | 6 +- docs/html/_modules/idtxl/idtxl_io.html | 6 +- docs/html/_modules/idtxl/idtxl_utils.html | 29 +- docs/html/_modules/idtxl/multivariate_mi.html | 24 +- .../html/_modules/idtxl/multivariate_pid.html | 6 +- docs/html/_modules/idtxl/multivariate_te.html | 38 +- .../html/_modules/idtxl/network_analysis.html | 22 +- .../_modules/idtxl/network_comparison.html | 6 +- .../_modules/idtxl/network_inference.html | 12 +- docs/html/_modules/idtxl/postprocessing.html | 6 +- docs/html/_modules/idtxl/results.html | 41 +- .../idtxl/single_process_analysis.html | 6 +- docs/html/_modules/idtxl/stats.html | 363 ++++++++---------- docs/html/_modules/idtxl/visualise_graph.html | 6 +- docs/html/_modules/index.html | 6 +- docs/html/_static/documentation_options.js | 2 +- docs/html/genindex.html | 6 +- docs/html/idtxl.html | 124 ++++-- docs/html/idtxl_data_class.html | 6 +- docs/html/idtxl_estimators.html | 6 +- docs/html/idtxl_helper.html | 48 +-- docs/html/idtxl_network_comparison.html | 6 +- docs/html/idtxl_network_inference.html | 6 +- docs/html/idtxl_postprocessing.html | 6 +- docs/html/idtxl_process_analysis.html | 6 +- docs/html/idtxl_results_class.html | 63 ++- docs/html/index.html | 6 +- docs/html/modules.html | 6 +- docs/html/objects.inv | Bin 2761 -> 2761 bytes docs/html/py-modindex.html | 6 +- docs/html/search.html | 6 +- docs/html/searchindex.js | 2 +- 50 files changed, 563 insertions(+), 515 deletions(-) diff --git a/docs/doctrees/environment.pickle b/docs/doctrees/environment.pickle index 9863270cf0dd7068c9d32998da6e6b2f726e3f64..e4299c3eb2af5a3ee48a3dba0db1c6eced1edd1a 100644 GIT binary patch delta 43487 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

Egm|=6)JV& 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 diff --git a/docs/doctrees/idtxl_helper.doctree b/docs/doctrees/idtxl_helper.doctree index 6c68fcd5948baa734a4a7d1db163998cded013f6..ab1fc9a0536f4e76147bfd41b2de085f4c257b65 100644 GIT binary patch delta 16781 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 diff --git a/docs/doctrees/idtxl_results_class.doctree b/docs/doctrees/idtxl_results_class.doctree index 78e4a618a8210ea67bcfb5cd8a76d6373840f31c..7877fd2dd0ab5320cd7bb759dcb1c24f6481923c 100644 GIT binary patch delta 3231 zcmZ{meNbFi6~MV?+1xW3W;7GDt&YmnBmjF2(49jrV-&^P9uJxdU(7 z!^_ufuOz{H0+%j|LW{fo)KG6NL0sTaq%=w}o zDzLCk*m3t3cwRij9_sa5$#5gy%!YLIr9p}4h+;vb1IumT!WYwEL^&?Gj@`$V1b&T) zRS(@2Wd^pkn?(|;&cRTH@t_(gofNj?gG2t18P#=BDcNpV;PYZivSpC9OUzayF2}Gg zMX;RdFsi&Fv0l0l%V@}?nR612)EN!;WI!T(Eyi|HW3$F%I~($kbRSmh%(%w@599p0 z*dgH_&44*&Rl58OyKE3Q7Y=2@cA@-7N1+&H=o#+Cg}ZErR6 z=!U(uoo4TKYJ1CuB9HbK+f!Lmgy-%EML0Q)CGijsG`4hb+1LnKCjsYj;8`&$0dvV< za0B3l7+_EWCg;Kh~$;Q>{x&U5yJx4m@B`Nx9O6 z%b%vQ;dfBRcy^jA`r{?LIAc5FW$f6{aH#uu|EXZ8`?3C$-NF7deUb8ossgQ&?(IT& z2~KRFD7#3h5hN8ETt$BR01P0;f5@lYZjM*V*dPkzHL{N%J`E7|Z&nVUK;~2&Z z8iprcly*{t`cIzdKIIQ_cx0JU)oB-aB-ZyFFeZ%HSh=yhmykD|8~6C2T4c(xtJf4D zkyc38o#0oTZY`5`9P1nUR_h-L4RyD*(29@s_tKH}YZD!veB7qhY#w^c z1P<&qLx#&38M@~(+K)O77|2bkCsrqRP~Ysq8-Ev-kmun-0o2t3^xsnGHI;g_{pIs$ zl|o-i>Qp24K|wgJIf+#{N_w_p)Wq1{E|TarqN%8RlQI*?z~(E&Adc#w04?QknDC(Sc4BGS@`*g)q7qK+sLBzVHJ zP5D~-k|XJ3{uRCm*}^l;My#rW3hj!v7`+kUR26h-vL?E=jry>p8ct`e5X(T9o?gG_ zucPv7VXhjc1>E-FQNO5yk33kIMB%+H{r0>olHnGG`~i3ilDy%?NAkkz>-j0ScwKn` zUBEhE_M&A*WZ`rF7GA7<5NcD*UTvER;#~EF)5f-ipMD_;XC8zOlbwfh2a4HJ`BpZP z-^zN0EGK*6#awlIMkJ=JZg!A|6LnzCswYLch@JeUEn=q^X%X9X1MhnnB@^#_TV$ZV z9`bG39{=txYJIrkqOgD z{}X+oFAnzw`C>W1Y6l1YWahUegWs%sa3oO3qcs0g6J$ZVcj3EDuti{EGqnB>8S|q; delta 2577 zcmbuAYiv|S6vvsf&%5oGZXZiw`?$5RuhP=<9U?31;6cQySK2c&L2?jMpjMjSQ&Ml1?Klx=c_niOv z&Hv2I&hFYLeUg&4sB9bSIP?U|9c3-}@+cd@ZGW*j_}*XaKEtw(g)cOL3t!C!kI^Q} zGIoryXYuM7YctdZmg3K2Y&Nd4fXl#hn=Bx|a0D8VF<669FBNWKunhNDp&By*s;~*5 z5nCiEz_$U)@Hq*bcnx5=Rvt9KLM*PKWY7ji95%pGSXqzhM%c<`;WI|Kiyg#|jBplT zGEp%ZEW#g5P>EB`P>=0qcpNX9A%HV2uoyR6pq=~3!(S|v)y%Y6p@o}?V44KA+($jP z(1mRV$VSNq-W$uGGS3RTA}8I-`76<3I+)rOBR2lPG8RU|SX3VRF#ihHcqUqU>$3kSI9HDs8-a#64+Jat&_J zgx6Rp%6Tx)ltp0=#vM=vb3Gc=gBKmJM`{t!CIQ9cSzu(16I2gg&4M{*oj8&WeJrdI z%{fHu_F!fa%z^a++0BuDyek(rNZSRsUpJde>L+esfePD3S%r;5boPoV@PTzuR2|?; z)Utu$)p;&{ z*eYEU>|b^EG*VxkU{j&ylS6c$cNW6);Gi?5vog6TZpX<*a61%vwQaSdqlEV0KoLAC z-Rjj2tir1um^q)sWnN_<%LjG}@&0+e&K)X-qtGh2<^qa0d9^#RQgE9%_lOh|$aOlh zkaQ7&Y|@duR7VD8z#FhbAhE&?M(7vJor2jfyqh z!WrFbF~v^`?gha;DY$)p7?ge#+!3973yCiY?n#}?>vhA2X2K~j`NZCqP@LiWWzY-R zKEAiWd?@FG5BtmM*WmVPn>*bnHg`Iyi+$SWl1$rN4$#4?Dj)^*0&Jc^@fx4Fxj{ju zPmO${5;~Kb{K}dA81EBG$1uAXm%XJzWVkY-v}+&XmZO+?;(<-KsvSdc&s3s95a} z(FX>qAx$yfpRH^evZ3ov=(1+`wR1^FM>SNL%gMatr(?MVzb#=F;{|$}F!&gtQhR5n*y;C7jq}@b=%4e&@3@fcHXSjU< zJkH<^KVDo2x6!`@St-PtH0Gq3GEiTLtG{CDFz8q2A6tRnTxU6WrGcLO>vzLqc8049 z6!}=8eUN+24;#V-k<)&3>}5`T_BS>Y&ow}m{WMP&HZ$sAzhB9GEKlhhO2Z>vY^n8o zo^nMla#E#z--PgMKb~&{zkUDB;CwvL%?g~?DK2{YozJwMe)nrVNiEX}S`gW%8Og<~ zd&u=Gi(rA%TsmH4;WMqsQmPf9eSoD8I|J|;KuziJm1ekP=*#1(Tt0kbqv1Wh(2Zqx%TDVN0zT;$$isTSE znX=y>izT9i!5}cFH!P(x;v3_M(7KLwp?JLWfsm@G%0oj_15=hqLytTd>Pm#V<(_DF zD4OU@^u(g&<2U_3^Ed8#*i5^>uT(j)tla2|>@B7HRk$!^>J6bv(t;`AY - idtxl.active_information_storage — IDTxl 1.5.1 documentation + idtxl.active_information_storage — IDTxl 1.6.0 documentation @@ -31,7 +31,7 @@

Navigation

  • modules |
  • - + @@ -741,7 +741,7 @@

    Navigation

  • modules |
  • - + diff --git a/docs/html/_modules/idtxl/bivariate_mi.html b/docs/html/_modules/idtxl/bivariate_mi.html index 53a8d8e..a8ec368 100644 --- a/docs/html/_modules/idtxl/bivariate_mi.html +++ b/docs/html/_modules/idtxl/bivariate_mi.html @@ -5,7 +5,7 @@ - idtxl.bivariate_mi — IDTxl 1.5.1 documentation + idtxl.bivariate_mi — IDTxl 1.6.0 documentation @@ -31,7 +31,7 @@

    Navigation

  • modules |
  • - + @@ -397,7 +397,7 @@

    Navigation

  • modules |
  • - + diff --git a/docs/html/_modules/idtxl/bivariate_pid.html b/docs/html/_modules/idtxl/bivariate_pid.html index 7ae3f85..92392a4 100644 --- a/docs/html/_modules/idtxl/bivariate_pid.html +++ b/docs/html/_modules/idtxl/bivariate_pid.html @@ -5,7 +5,7 @@ - idtxl.bivariate_pid — IDTxl 1.5.1 documentation + idtxl.bivariate_pid — IDTxl 1.6.0 documentation @@ -31,7 +31,7 @@

    Navigation

  • modules |
  • - + @@ -387,7 +387,7 @@

    Navigation

  • modules |
  • - + diff --git a/docs/html/_modules/idtxl/bivariate_te.html b/docs/html/_modules/idtxl/bivariate_te.html index fb12e49..e2cd536 100644 --- a/docs/html/_modules/idtxl/bivariate_te.html +++ b/docs/html/_modules/idtxl/bivariate_te.html @@ -5,7 +5,7 @@ - idtxl.bivariate_te — IDTxl 1.5.1 documentation + idtxl.bivariate_te — IDTxl 1.6.0 documentation @@ -31,7 +31,7 @@

    Navigation

  • modules |
  • - + @@ -400,7 +400,7 @@

    Navigation

  • modules |
  • - + diff --git a/docs/html/_modules/idtxl/data.html b/docs/html/_modules/idtxl/data.html index ecb1981..8b2cd38 100644 --- a/docs/html/_modules/idtxl/data.html +++ b/docs/html/_modules/idtxl/data.html @@ -5,7 +5,7 @@ - idtxl.data — IDTxl 1.5.1 documentation + idtxl.data — IDTxl 1.6.0 documentation @@ -31,7 +31,7 @@

    Navigation

  • modules |
  • - + @@ -45,6 +45,7 @@

    Navigation

    Source code for idtxl.data

     """Provide data structures for IDTxl analysis."""
     import numpy as np
    +
     from . import idtxl_utils as utils
     
     VERBOSE = False
    @@ -190,7 +191,7 @@ 

    Source code for idtxl.data

                     must have the same length as number of dimensions in data
             """
             if len(dim_order) > 3:
    -            raise RuntimeError("dim_order can not have more than three " "entries")
    +            raise RuntimeError("dim_order can not have more than three entries")
             if len(dim_order) != data.ndim:
                 raise RuntimeError(
                     f"Data array dimension ({data.ndim}) and length of "
    @@ -202,8 +203,8 @@ 

    Source code for idtxl.data

             data_ordered = self._reorder_data(data, dim_order)
             self._set_data_size(data_ordered)
             print(
    -            "Adding data with properties: {0} processes, {1} samples, {2} "
    -            "replications".format(self.n_processes, self.n_samples, self.n_replications)
    +            f"Adding data with properties: {self.n_processes} processes, {self.n_samples} "
    +            f"samples, {self.n_replications} replications"
             )
             try:
                 delattr(self, "data")
    @@ -301,7 +302,7 @@ 

    Source code for idtxl.data

                     samples * no.replications) x number of indices
             """
             if not hasattr(self, "data"):
    -            raise AttributeError("No data has been added to this Data() " "instance.")
    +            raise AttributeError("No data has been added to this Data() instance.")
             # Return None if index list is empty.
             if not idx_list:
                 return None, None
    @@ -341,12 +342,11 @@ 

    Source code for idtxl.data

                         realisations[r : r + n_real_time, i] = self.data[
                             idx[0], idx[1] : last_sample, replication
                         ]
    -                except IndexError:
    +                except IndexError as e:
                         raise IndexError(
    -                        "You tried to access variable {0} in a "
    -                        "data set with {1} processes and {2} "
    -                        "samples.".format(idx, self.n_processes, self.n_samples)
    -                    )
    +                        f"You tried to access variable {idx} in a data set with {self.n_processes} "
    +                        f"processes and {self.n_samples} samples."
    +                    ) from e
                     r += n_real_time
     
                 assert not np.isnan(
    @@ -423,14 +423,11 @@ 

    Source code for idtxl.data

     
             try:
                 data_slice = self.data[process, offset_samples:, replication_index]
    -        except IndexError:
    +        except IndexError as e:
                 raise IndexError(
    -                "You tried to access process {0} with an offset "
    -                "of {1} in a data set of {2} processes and {3} "
    -                "samples.".format(
    -                    process, offset_samples, self.n_processes, self.n_samples
    -                )
    -            )
    +                "You tried to access process {process} with an offset of {offset_samples} in a data set "
    +                "of {self.n_processes} processes and {self.n_samples} samples."
    +            ) from e
             assert not np.isnan(
                 data_slice
             ).any(), "There are nans in the retrieved data slice."
    @@ -751,22 +748,22 @@ 

    Source code for idtxl.data

             elif perm_type == "circular":
                 max_shift = perm_settings["max_shift"]
                 if not isinstance(max_shift, int) or max_shift < 1:
    -                raise TypeError(" " "max_shift" " has to be an int > 0.")
    +                raise TypeError("'max_shift' has to be an int > 0.")
                 perm = self._circular_shift(n_samples, max_shift)[0]
     
             elif perm_type == "block":
                 block_size = perm_settings["block_size"]
                 perm_range = perm_settings["perm_range"]
                 if not isinstance(block_size, int) or block_size < 1:
    -                raise TypeError(" " "block_size" " has to be an int > 0.")
    +                raise TypeError("'block_size' has to be an int > 0.")
                 if not isinstance(perm_range, int) or perm_range < 1:
    -                raise TypeError(" " "perm_range" " has to be an int > 0.")
    +                raise TypeError("'perm_range' has to be an int > 0.")
                 perm = self._swap_blocks(n_samples, block_size, perm_range)
     
             elif perm_type == "local":
                 perm_range = perm_settings["perm_range"]
                 if not isinstance(perm_range, int) or perm_range < 1:
    -                raise TypeError(" " "perm_range" " has to be an int > 0.")
    +                raise TypeError("'perm_range' has to be an int > 0.")
                 perm = self._swap_local(n_samples, perm_range)
     
             else:
    @@ -1133,7 +1130,7 @@ 

    Navigation

  • modules |
  • - + diff --git a/docs/html/_modules/idtxl/embedding_optimization_ais_Rudelt.html b/docs/html/_modules/idtxl/embedding_optimization_ais_Rudelt.html index 7888684..4d38b07 100644 --- a/docs/html/_modules/idtxl/embedding_optimization_ais_Rudelt.html +++ b/docs/html/_modules/idtxl/embedding_optimization_ais_Rudelt.html @@ -5,7 +5,7 @@ - idtxl.embedding_optimization_ais_Rudelt — IDTxl 1.5.1 documentation + idtxl.embedding_optimization_ais_Rudelt — IDTxl 1.6.0 documentation @@ -31,7 +31,7 @@

    Navigation

  • modules |
  • - + @@ -1311,7 +1311,7 @@

    Navigation

  • modules |
  • - + diff --git a/docs/html/_modules/idtxl/estimators_Rudelt.html b/docs/html/_modules/idtxl/estimators_Rudelt.html index b352b7b..e3e2a92 100644 --- a/docs/html/_modules/idtxl/estimators_Rudelt.html +++ b/docs/html/_modules/idtxl/estimators_Rudelt.html @@ -5,7 +5,7 @@ - idtxl.estimators_Rudelt — IDTxl 1.5.1 documentation + idtxl.estimators_Rudelt — IDTxl 1.6.0 documentation @@ -31,7 +31,7 @@

    Navigation

  • modules |
  • - + @@ -46,14 +46,16 @@

    Source code for idtxl.estimators_Rudelt

     """Provide HDE estimators."""
     
     import logging
    -import numpy as np
    -from scipy.optimize import newton, minimize
     import sys
    -from sys import stderr
    -from idtxl.estimator import Estimator
    -import idtxl.hde_utils as utl
     from collections import Counter
    +from sys import stderr
    +
     import mpmath as mp
    +import numpy as np
    +from scipy.optimize import minimize, newton
    +
    +import idtxl.hde_utils as utl
    +from idtxl.estimator import Estimator
     
     FAST_EMBEDDING_AVAILABLE = True
     try:
    @@ -62,9 +64,9 @@ 

    Source code for idtxl.estimators_Rudelt

         FAST_EMBEDDING_AVAILABLE = False
         print(
             """
    -    Error importing Cython fast embedding module for HDE estimator.\n
    -    When running the HDE estimator, the slow Python implementation for optimizing the HDE embedding will be used,\n
    -    this may take a long time. Other estimators are not affected.\n
    +    Error importing Cython fast embedding module for HDE estimator.
    +    When running the HDE estimator, the slow Python implementation for optimizing the HDE embedding will be used,
    +    this may take a long time. Other estimators are not affected.
         """,
             file=stderr,
             flush=True,
    @@ -1350,7 +1352,7 @@ 

    Navigation

  • modules |
  • - + diff --git a/docs/html/_modules/idtxl/estimators_jidt.html b/docs/html/_modules/idtxl/estimators_jidt.html index 907b2e5..2f67f8f 100644 --- a/docs/html/_modules/idtxl/estimators_jidt.html +++ b/docs/html/_modules/idtxl/estimators_jidt.html @@ -5,7 +5,7 @@ - idtxl.estimators_jidt — IDTxl 1.5.1 documentation + idtxl.estimators_jidt — IDTxl 1.6.0 documentation @@ -31,7 +31,7 @@

    Navigation

  • modules |
  • - + @@ -1874,7 +1874,7 @@

    Navigation

  • modules |
  • - + diff --git a/docs/html/_modules/idtxl/estimators_mpi.html b/docs/html/_modules/idtxl/estimators_mpi.html index e6752b2..b400224 100644 --- a/docs/html/_modules/idtxl/estimators_mpi.html +++ b/docs/html/_modules/idtxl/estimators_mpi.html @@ -5,7 +5,7 @@ - idtxl.estimators_mpi — IDTxl 1.5.1 documentation + idtxl.estimators_mpi — IDTxl 1.6.0 documentation @@ -31,7 +31,7 @@

    Navigation

  • modules |
  • - + @@ -267,7 +267,7 @@

    Navigation

  • modules |
  • - + diff --git a/docs/html/_modules/idtxl/estimators_multivariate_pid.html b/docs/html/_modules/idtxl/estimators_multivariate_pid.html index 66407fc..61906c0 100644 --- a/docs/html/_modules/idtxl/estimators_multivariate_pid.html +++ b/docs/html/_modules/idtxl/estimators_multivariate_pid.html @@ -5,7 +5,7 @@ - idtxl.estimators_multivariate_pid — IDTxl 1.5.1 documentation + idtxl.estimators_multivariate_pid — IDTxl 1.6.0 documentation @@ -31,7 +31,7 @@

    Navigation

  • modules |
  • - + @@ -53,6 +53,7 @@

    Source code for idtxl.estimators_multivariate_pid

    http://arxiv.org/abs/2002.03356 """ import numpy as np + from . import lattices as lt from . import pid_goettingen from .estimator import Estimator @@ -92,7 +93,7 @@

    Source code for idtxl.estimators_multivariate_pid

    def __init__(self, settings): # get estimation parameters self.settings = settings.copy() - self.settings.setdefault('verbose', False) + self.settings.setdefault("verbose", False)
    [docs] def is_parallel(): return False
    @@ -101,7 +102,8 @@

    Source code for idtxl.estimators_multivariate_pid

    return False
    [docs] def estimate(self, s, t): - """ + """Estimate SxPID from list of sources and a target + Args: s : list of numpy arrays 1D arrays containing realizations of a discrete random variable @@ -109,16 +111,12 @@

    Source code for idtxl.estimators_multivariate_pid

    1D array containing realizations of a discrete random variable Returns: - dict of dict - { - 'ptw' -> { realization -> {alpha -> [float, float, float]} } - - 'avg' -> {alpha -> [float, float, float]} - } - where the list of floats is ordered - [informative, misinformative, informative - misinformative] - ptw stands for pointwise decomposition - avg stands for average decomposition + dict + SxPID results, with entries + - 'ptw' -> { realization -> {alpha -> [float, float, float]}}: pointwise decomposition + - 'avg' -> {alpha -> [float, float, float]}: average decomposition + + the list of floats is ordered [informative, misinformative, informative - misinformative] """ s, t, self.settings = _check_input(s, t, self.settings) pdf = _get_pdf_dict(s, t) @@ -135,19 +133,20 @@

    Source code for idtxl.estimators_multivariate_pid

    pdf_orig=pdf, chld=lattices[num_source_vars][0], achain=lattices[num_source_vars][1], - printing=self.settings['verbose']) + printing=self.settings["verbose"], + ) # TODO AskM: Trivariate: does it make sense to name the alphas # for example shared_syn_s1_s2__syn_s1_s3 ? results = { - 'ptw': retval_ptw, - 'avg': retval_avg, + "ptw": retval_ptw, + "avg": retval_avg, } return results
    def _get_pdf_dict(s, t): - """"Write probability mass function estimated via counting to a dict.""" + """ "Write probability mass function estimated via counting to a dict.""" # Create dictionary with probability mass function counts = dict() n_samples = s[0].shape[0] @@ -171,10 +170,10 @@

    Source code for idtxl.estimators_multivariate_pid

    """Check input to PID estimators.""" # Check if inputs are numpy arrays. if type(t) != np.ndarray: - raise TypeError('Input t must be a numpy array.') + raise TypeError("Input t must be a numpy array.") for i in range(len(s)): if type(s[i]) != np.ndarray: - raise TypeError('All inputs s{0} must be numpy arrays.'.format(i+1)) + raise TypeError("All inputs s{0} must be numpy arrays.".format(i + 1)) # In general, IDTxl expects 2D inputs because JIDT/JPYPE only accepts those # and we have a multivariate approach, i.e., a vector is a special case of @@ -191,34 +190,38 @@

    Source code for idtxl.estimators_multivariate_pid

    for col in range(1, s[i].shape[1]): alph_col = len(np.unique(s[i][:, col])) si_joint, alph_new = _join_variables( - si_joint, s[i][:, col], alph_new, alph_col) - settings['alph_s'+str(i+1)] = alph_new + si_joint, s[i][:, col], alph_new, alph_col + ) + settings["alph_s" + str(i + 1)] = alph_new else: - raise ValueError('Input source {0} s{0} has to be a 1D or 2D ' - 'numpy array.'.format(i+1)) + raise ValueError( + "Input source {0} s{0} has to be a 1D or 2D " + "numpy array.".format(i + 1) + ) if t.ndim != 1: if t.shape[1] == 1: t = np.squeeze(t) else: # For now we only allow 1D-targets - raise ValueError('Input target t has to be a vector ' - '(t.shape[1]=1).') + raise ValueError("Input target t has to be a vector " "(t.shape[1]=1).") # Check types of remaining inputs. if type(settings) != dict: - raise TypeError('The settings argument should be a dictionary.') + raise TypeError("The settings argument should be a dictionary.") for i in range(len(s)): if not issubclass(s[i].dtype.type, np.integer): - raise TypeError('Input s{0} (source {0}) must be an integer numpy ' - 'array.'.format(i+1)) + raise TypeError( + "Input s{0} (source {0}) must be an integer numpy " + "array.".format(i + 1) + ) # ^ for if not issubclass(t.dtype.type, np.integer): - raise TypeError('Input t (target) must be an integer numpy array.') + raise TypeError("Input t (target) must be an integer numpy array.") # Check if variables have equal length. for i in range(len(s)): if len(t) != len(s[i]): - raise ValueError('Number of samples s and t must be equal') + raise ValueError("Number of samples s and t must be equal") return s, t, settings
    @@ -252,7 +255,7 @@

    Navigation

  • modules |
  • - + diff --git a/docs/html/_modules/idtxl/estimators_opencl.html b/docs/html/_modules/idtxl/estimators_opencl.html index e1d13da..0d1397d 100644 --- a/docs/html/_modules/idtxl/estimators_opencl.html +++ b/docs/html/_modules/idtxl/estimators_opencl.html @@ -5,7 +5,7 @@ - idtxl.estimators_opencl — IDTxl 1.5.1 documentation + idtxl.estimators_opencl — IDTxl 1.6.0 documentation @@ -31,7 +31,7 @@

    Navigation

  • modules |
  • - + @@ -889,7 +889,7 @@

    Navigation

  • modules |
  • - + diff --git a/docs/html/_modules/idtxl/estimators_pid.html b/docs/html/_modules/idtxl/estimators_pid.html index 45b51bb..73189db 100644 --- a/docs/html/_modules/idtxl/estimators_pid.html +++ b/docs/html/_modules/idtxl/estimators_pid.html @@ -5,7 +5,7 @@ - idtxl.estimators_pid — IDTxl 1.5.1 documentation + idtxl.estimators_pid — IDTxl 1.6.0 documentation @@ -31,7 +31,7 @@

    Navigation

  • modules |
  • - + @@ -712,7 +712,7 @@

    Navigation

  • modules |
  • - + diff --git a/docs/html/_modules/idtxl/estimators_python.html b/docs/html/_modules/idtxl/estimators_python.html index ef7d804..af04df4 100644 --- a/docs/html/_modules/idtxl/estimators_python.html +++ b/docs/html/_modules/idtxl/estimators_python.html @@ -5,7 +5,7 @@ - idtxl.estimators_python — IDTxl 1.5.1 documentation + idtxl.estimators_python — IDTxl 1.6.0 documentation @@ -31,7 +31,7 @@

    Navigation

  • modules |
  • - + @@ -235,7 +235,7 @@

    Navigation

  • modules |
  • - + diff --git a/docs/html/_modules/idtxl/idtxl_exceptions.html b/docs/html/_modules/idtxl/idtxl_exceptions.html index fab5651..2859fad 100644 --- a/docs/html/_modules/idtxl/idtxl_exceptions.html +++ b/docs/html/_modules/idtxl/idtxl_exceptions.html @@ -5,7 +5,7 @@ - idtxl.idtxl_exceptions — IDTxl 1.5.1 documentation + idtxl.idtxl_exceptions — IDTxl 1.6.0 documentation @@ -31,7 +31,7 @@

    Navigation

  • modules |
  • - + @@ -114,7 +114,7 @@

    Navigation

  • modules |
  • - + diff --git a/docs/html/_modules/idtxl/idtxl_io.html b/docs/html/_modules/idtxl/idtxl_io.html index 6883e51..7efa6bf 100644 --- a/docs/html/_modules/idtxl/idtxl_io.html +++ b/docs/html/_modules/idtxl/idtxl_io.html @@ -5,7 +5,7 @@ - idtxl.idtxl_io — IDTxl 1.5.1 documentation + idtxl.idtxl_io — IDTxl 1.6.0 documentation @@ -31,7 +31,7 @@

    Navigation

  • modules |
  • - + @@ -643,7 +643,7 @@

    Navigation

  • modules |
  • - + diff --git a/docs/html/_modules/idtxl/idtxl_utils.html b/docs/html/_modules/idtxl/idtxl_utils.html index 8e8fc0c..3706152 100644 --- a/docs/html/_modules/idtxl/idtxl_utils.html +++ b/docs/html/_modules/idtxl/idtxl_utils.html @@ -5,7 +5,7 @@ - idtxl.idtxl_utils — IDTxl 1.5.1 documentation + idtxl.idtxl_utils — IDTxl 1.6.0 documentation @@ -31,7 +31,7 @@

    Navigation

  • modules |
  • - + @@ -44,11 +44,12 @@

    Navigation

    Source code for idtxl.idtxl_utils

     """Provide IDTxl utility functions."""
    -import pprint
     import copy as cp
    -import numpy as np
    +import pprint
     import threading
     
    +import numpy as np
    +
     
     
    [docs]def swap_chars(s, i_1, i_2): """Swap to characters in a string. @@ -361,14 +362,7 @@

    Source code for idtxl.idtxl_utils

         for k in intersect_keys:
             if np.array(dict_1[k] != dict_2[k]).any():
                 print(
    -                "Unequal entries for key "
    -                "{0}"
    -                ": dict_1: "
    -                "{1}"
    -                ", dict_2:"
    -                " "
    -                "{2}"
    -                ".".format(k, dict_1[k], dict_2[k])
    +                f"Unequal entries for key {k}: dict_1: {dict_1[k]}, dict_2: {dict_2[k]}"
                 )
                 return True
         return False
    @@ -381,9 +375,12 @@

    Source code for idtxl.idtxl_utils

     
     
    [docs]class timeout(object): """Context manager for a timeout using threading module. - args: - timeout_duration: float, number of seconds to wait before timeout is triggered - exception_message: string, message to put in the exception + + Args: + timeout_duration: float + number of seconds to wait before timeout is triggered + exception_message : string + message to put in the exception """ def __init__(self, timeout_duration, exception_message="Timeout"): @@ -431,7 +428,7 @@

    Navigation

  • modules |
  • - + diff --git a/docs/html/_modules/idtxl/multivariate_mi.html b/docs/html/_modules/idtxl/multivariate_mi.html index 8eb897e..d2f713d 100644 --- a/docs/html/_modules/idtxl/multivariate_mi.html +++ b/docs/html/_modules/idtxl/multivariate_mi.html @@ -5,7 +5,7 @@ - idtxl.multivariate_mi — IDTxl 1.5.1 documentation + idtxl.multivariate_mi — IDTxl 1.6.0 documentation @@ -31,7 +31,7 @@

    Navigation

  • modules |
  • - + @@ -52,9 +52,9 @@

    Source code for idtxl.multivariate_mi

     Note:
         Written for Python 3.4+
     """
    -from .stats import network_fdr
     from .network_inference import NetworkInferenceMI, NetworkInferenceMultivariate
     from .results import ResultsNetworkInference
    +from .stats import network_fdr
     
     
     
    [docs]class MultivariateMI(NetworkInferenceMI, NetworkInferenceMultivariate): @@ -174,20 +174,18 @@

    Source code for idtxl.multivariate_mi

     
             # Check which targets and sources are requested for analysis.
             if targets == "all":
    -            targets = [t for t in range(data.n_processes)]
    +            targets = list(range(data.n_processes))
             if sources == "all":
                 sources = ["all" for t in targets]
    -        if (type(sources) is list) and (type(sources[0]) is int):
    +        elif isinstance(sources, list) and isinstance(sources[0], int):
                 sources = [sources for t in targets]
    -        if (type(sources) is list) and (type(sources[0]) is list):
    +        elif isinstance(sources, list) and isinstance(sources[0], list):
                 pass
             else:
    -            ValueError("Sources was not specified correctly: {0}.".format(sources))
    -        assert len(sources) == len(targets), (
    -            "List of targets and list of "
    -            "sources have to have the same "
    -            "same length"
    -        )
    +            raise ValueError(f"Sources was not specified correctly: {sources}.")
    +        assert len(sources) == len(
    +            targets
    +        ), "List of targets and list of sources have to have the same length"
     
             # Check and set defaults for checkpointing.
             settings = self._set_checkpointing_defaults(settings, data, sources, targets)
    @@ -399,7 +397,7 @@ 

    Navigation

  • modules |
  • - + diff --git a/docs/html/_modules/idtxl/multivariate_pid.html b/docs/html/_modules/idtxl/multivariate_pid.html index 309215d..9ea31ee 100644 --- a/docs/html/_modules/idtxl/multivariate_pid.html +++ b/docs/html/_modules/idtxl/multivariate_pid.html @@ -5,7 +5,7 @@ - idtxl.multivariate_pid — IDTxl 1.5.1 documentation + idtxl.multivariate_pid — IDTxl 1.6.0 documentation @@ -31,7 +31,7 @@

    Navigation

  • modules |
  • - + @@ -394,7 +394,7 @@

    Navigation

  • modules |
  • - + diff --git a/docs/html/_modules/idtxl/multivariate_te.html b/docs/html/_modules/idtxl/multivariate_te.html index 229e31e..cce9c69 100644 --- a/docs/html/_modules/idtxl/multivariate_te.html +++ b/docs/html/_modules/idtxl/multivariate_te.html @@ -5,7 +5,7 @@ - idtxl.multivariate_te — IDTxl 1.5.1 documentation + idtxl.multivariate_te — IDTxl 1.6.0 documentation @@ -31,7 +31,7 @@

    Navigation

  • modules |
  • - + @@ -52,9 +52,9 @@

    Source code for idtxl.multivariate_te

     Note:
         Written for Python 3.4+
     """
    -from .network_inference import NetworkInferenceTE, NetworkInferenceMultivariate
    -from .stats import network_fdr
    +from .network_inference import NetworkInferenceMultivariate, NetworkInferenceTE
     from .results import ResultsNetworkInference
    +from .stats import network_fdr
     
     
     
    [docs]class MultivariateTE(NetworkInferenceTE, NetworkInferenceMultivariate): @@ -177,20 +177,18 @@

    Source code for idtxl.multivariate_te

     
             # Check which targets and sources are requested for analysis.
             if targets == "all":
    -            targets = [t for t in range(data.n_processes)]
    +            targets = list(range(data.n_processes))
             if sources == "all":
                 sources = ["all" for t in targets]
    -        if (type(sources) is list) and (type(sources[0]) is int):
    +        elif isinstance(sources, list) and isinstance(sources[0], int):
                 sources = [sources for t in targets]
    -        if (type(sources) is list) and (type(sources[0]) is list):
    +        elif isinstance(sources, list) and isinstance(sources[0], list):
                 pass
             else:
    -            ValueError("Sources was not specified correctly: {0}.".format(sources))
    -        assert len(sources) == len(targets), (
    -            "List of targets and list of "
    -            "sources have to have the same "
    -            "same length"
    -        )
    +            raise ValueError(f"Sources was not specified correctly: {sources}.")
    +        assert len(sources) == len(
    +            targets
    +        ), "List of targets and list of sources have to have the same length"
     
             # Check and set defaults for checkpointing.
             settings = self._set_checkpointing_defaults(settings, data, sources, targets)
    @@ -201,16 +199,10 @@ 

    Source code for idtxl.multivariate_te

                 n_realisations=data.n_realisations(),
                 normalised=data.normalise,
             )
    -        for t in range(len(targets)):
    +        for t, target in enumerate(targets):
                 if settings["verbose"]:
    -                print(
    -                    "\n####### analysing target with index {0} from list {1}".format(
    -                        t, targets
    -                    )
    -                )
    -            res_single = self.analyse_single_target(
    -                settings, data, targets[t], sources[t]
    -            )
    +                print(f"\n####### analysing target with index {t} from list {targets}")
    +            res_single = self.analyse_single_target(settings, data, target, sources[t])
                 results.combine_results(res_single)
     
             # Get no. realisations actually used for estimation from single target
    @@ -415,7 +407,7 @@ 

    Navigation

  • modules |
  • - + diff --git a/docs/html/_modules/idtxl/network_analysis.html b/docs/html/_modules/idtxl/network_analysis.html index e0c61a4..77eb9a2 100644 --- a/docs/html/_modules/idtxl/network_analysis.html +++ b/docs/html/_modules/idtxl/network_analysis.html @@ -5,7 +5,7 @@ - idtxl.network_analysis — IDTxl 1.5.1 documentation + idtxl.network_analysis — IDTxl 1.6.0 documentation @@ -31,7 +31,7 @@

    Navigation

  • modules |
  • - + @@ -45,17 +45,19 @@

    Navigation

    Source code for idtxl.network_analysis

     """Parent class for network inference and network comparison.
     """
    -import os.path
    -from datetime import datetime
    -from shutil import copyfile
    -from pprint import pprint
     import ast
     import copy as cp
     import itertools as it
    +import os.path
    +from datetime import datetime
    +from pprint import pprint
    +from shutil import copyfile
    +
     import numpy as np
    -from .estimator import get_estimator
    -from . import idtxl_utils as utils
    +
     from . import idtxl_io as io
    +from . import idtxl_utils as utils
    +from .estimator import get_estimator
     
     
     
    [docs]class NetworkAnalysis: @@ -707,7 +709,7 @@

    Source code for idtxl.network_analysis

     
             Args:
                 file_path : str
    -                path to checkpoint file (excluding extension: *.ckp)
    +                path to checkpoint file (excluding extension: .ckp)
             """
     
             # Read checkpoint
    @@ -783,7 +785,7 @@ 

    Navigation

  • modules |
  • - + diff --git a/docs/html/_modules/idtxl/network_comparison.html b/docs/html/_modules/idtxl/network_comparison.html index dbd6d57..cf6801d 100644 --- a/docs/html/_modules/idtxl/network_comparison.html +++ b/docs/html/_modules/idtxl/network_comparison.html @@ -5,7 +5,7 @@ - idtxl.network_comparison — IDTxl 1.5.1 documentation + idtxl.network_comparison — IDTxl 1.6.0 documentation @@ -31,7 +31,7 @@

    Navigation

  • modules |
  • - + @@ -1115,7 +1115,7 @@

    Navigation

  • modules |
  • - + diff --git a/docs/html/_modules/idtxl/network_inference.html b/docs/html/_modules/idtxl/network_inference.html index dcf79de..2db0a45 100644 --- a/docs/html/_modules/idtxl/network_inference.html +++ b/docs/html/_modules/idtxl/network_inference.html @@ -5,7 +5,7 @@ - idtxl.network_inference — IDTxl 1.5.1 documentation + idtxl.network_inference — IDTxl 1.6.0 documentation @@ -31,7 +31,7 @@

    Navigation

  • modules |
  • - + @@ -45,9 +45,10 @@

    Navigation

    Source code for idtxl.network_inference

     """Parent class for all network inference."""
     import numpy as np
    -from .network_analysis import NetworkAnalysis
    -from . import stats
    +
     from . import idtxl_exceptions as ex
    +from . import stats
    +from .network_analysis import NetworkAnalysis
     
     
     
    [docs]class NetworkInference(NetworkAnalysis): @@ -280,6 +281,7 @@

    Source code for idtxl.network_inference

         def _remove_non_significant(self, s, p, stat):
             # Remove non-significant sources from the candidate set. Loop
             # backwards over the candidates to remove them iteratively.
    +        print(f"removing {np.sum(np.invert(s))} variables after seq. max stats")
             for i in range(s.shape[0] - 1, -1, -1):
                 if not s[i]:
                     self._remove_selected_var(self.selected_vars_sources[i])
    @@ -1269,7 +1271,7 @@ 

    Navigation

  • modules |
  • - + diff --git a/docs/html/_modules/idtxl/postprocessing.html b/docs/html/_modules/idtxl/postprocessing.html index 8aaa192..c31edb8 100644 --- a/docs/html/_modules/idtxl/postprocessing.html +++ b/docs/html/_modules/idtxl/postprocessing.html @@ -5,7 +5,7 @@ - idtxl.postprocessing — IDTxl 1.5.1 documentation + idtxl.postprocessing — IDTxl 1.6.0 documentation @@ -31,7 +31,7 @@

    Navigation

  • modules |
  • - + @@ -1726,7 +1726,7 @@

    Navigation

  • modules |
  • - + diff --git a/docs/html/_modules/idtxl/results.html b/docs/html/_modules/idtxl/results.html index 9369472..eaf93d9 100644 --- a/docs/html/_modules/idtxl/results.html +++ b/docs/html/_modules/idtxl/results.html @@ -5,7 +5,7 @@ - idtxl.results — IDTxl 1.5.1 documentation + idtxl.results — IDTxl 1.6.0 documentation @@ -31,7 +31,7 @@

    Navigation

  • modules |
  • - + @@ -44,10 +44,12 @@

    Navigation

    Source code for idtxl.results

     """Provide results class for IDTxl network analysis."""
    +import copy as cp
     import sys
     import warnings
    -import copy as cp
    +
     import numpy as np
    +
     from . import idtxl_utils as utils
     
     warnings.simplefilter(action="ignore", category=FutureWarning)
    @@ -1203,38 +1205,39 @@ 

    Source code for idtxl.results

                     (result['selected_vars']) or via dot-notation
                     (result.selected_vars). Contains keys
     
    -                Process : int
    +                - Process : int
                         Process that was optimized
    -                estimation_method : String
    +                - estimation_method : String
                         Estimation method that was used for optimization
    -                T_D : float
    +                - T_D : float
                         Estimated optimal value for the temporal depth TD
    -                tau_R :
    +                - tau_R :
                         Information timescale tau_R, a characteristic timescale of history
                         dependence similar to an autocorrelation time.
    -                R_tot : float
    +                - R_tot : float
                         Estimated value for the total history dependence Rtot,
    -                AIS_tot : float
    +                - AIS_tot : float
                         Estimated value for the total active information storage
    -                opt_number_of_bins_d : int
    +                - opt_number_of_bins_d : int
                         Number of bins d for the embedding that yields (RÌ‚tot ,TÌ‚D)
    -                opt_scaling_k : int
    +                - opt_scaling_k : int
                         Scaling exponent κ for the embedding that yields (R̂tot , T̂D)
    -                opt_first_bin_size : int
    +                - opt_first_bin_size : int
                         Size of the first bin Ï„1 for the embedding that yields (RÌ‚tot , TÌ‚D ),
    -                history_dependence : array with floating-point values
    +                - history_dependence : array with floating-point values
                         Estimated history dependence for each embedding
    -                firing_rate : float
    +                - firing_rate : float
                         Firing rate of the neuron/ spike train
    -                recording_length : float
    +                - recording_length : float
                         Length of the recording (in seconds)
    -                H_spiking : float
    +                - H_spiking : float
                         Entropy of the spike times
     
                     if analyse_auto_MI was set to True additionally:
    -                auto_MI : dict
    +
    +                - auto_MI : dict
                         numpy array of MI values for each delay
    -                auto_MI_delays : list of int
    +                - auto_MI_delays : list of int
                         list of delays depending on the given auto_MI_bin_sizes and auto_MI_max_delay
     
             """
    @@ -1279,7 +1282,7 @@ 

    Navigation

  • modules |
  • - + diff --git a/docs/html/_modules/idtxl/single_process_analysis.html b/docs/html/_modules/idtxl/single_process_analysis.html index d696b68..436434d 100644 --- a/docs/html/_modules/idtxl/single_process_analysis.html +++ b/docs/html/_modules/idtxl/single_process_analysis.html @@ -5,7 +5,7 @@ - idtxl.single_process_analysis — IDTxl 1.5.1 documentation + idtxl.single_process_analysis — IDTxl 1.6.0 documentation @@ -31,7 +31,7 @@

    Navigation

  • modules |
  • - + @@ -82,7 +82,7 @@

    Navigation

  • modules |
  • - + diff --git a/docs/html/_modules/idtxl/stats.html b/docs/html/_modules/idtxl/stats.html index 145f8fb..0b8cf67 100644 --- a/docs/html/_modules/idtxl/stats.html +++ b/docs/html/_modules/idtxl/stats.html @@ -5,7 +5,7 @@ - idtxl.stats — IDTxl 1.5.1 documentation + idtxl.stats — IDTxl 1.6.0 documentation @@ -31,7 +31,7 @@

    Navigation

  • modules |
  • - + @@ -44,10 +44,13 @@

    Navigation

    Source code for idtxl.stats

     """Provide statistics functions."""
    +# pylint: disable=protected-access
     import copy as cp
    +
     import numpy as np
    -from . import idtxl_utils as utils
    +
     from . import idtxl_exceptions as ex
    +from . import idtxl_utils as utils
     
     
     
    [docs]def ais_fdr(settings=None, *results): @@ -104,28 +107,26 @@

    Source code for idtxl.stats

         process_idx = np.arange(0).astype(int)
         n_perm = np.arange(0).astype(int)
         for process in results_comb.processes_analysed:
    -        if results_comb._single_process[process].ais_sign:
    -            pval = np.append(pval, results_comb._single_process[process].ais_pval)
    -            process_idx = np.append(process_idx, process)
    -            n_perm = np.append(n_perm, results_comb.settings.n_perm_mi)
    +        next_pval = results_comb._single_process[process].ais_pval
    +        pval = np.append(pval, next_pval if next_pval is not None else 1)
    +        process_idx = np.append(process_idx, process)
    +        n_perm = np.append(n_perm, results_comb.settings.n_perm_mi)
     
         if pval.size == 0:
             print("FDR correction: no links in final results ...\n")
             results_comb._add_fdr(fdr=None, alpha=alpha, constant=constant)
             return results_comb
     
    -    sign, thresh = _perform_fdr_corretion(pval, constant, alpha)
    +    sign, thresh = _perform_fdr_corretion(
    +        pval, constant, alpha, len(results_comb.processes_analysed)
    +    )
     
         # If the number of permutations for calculating p-values for individual
         # variables is too low, return without performing any correction.
         if (1 / min(n_perm)) > thresh[0]:
             print(
    -            "WARNING: Number of permutations ("
    -            "n_perm_max_seq"
    -            ") for at "
    -            "least one target is too low to allow for FDR correction "
    -            "(FDR-threshold: {0:.4f}, min. theoretically possible p-value: "
    -            "{1}).".format(thresh[0], 1 / min(n_perm))
    +            "WARNING: Number of permutations ('n_perm_max_seq') for at least one target is too low to allow for "
    +            f"FDR correction (FDR-threshold: {thresh[0]:.4f}, min. theoretically possible p-value: {1 / min(n_perm)})."
             )
             results_comb._add_fdr(fdr=None, alpha=alpha, constant=constant)
             return results_comb
    @@ -207,23 +208,38 @@ 

    Source code for idtxl.stats

         n_perm = np.arange(0).astype(int)
         cands = []
         if correct_by_target:  # whole target
    +        # The total number of tests is the number of targets
    +        n_tests = len(results_comb.targets_analysed)
    +
             for target in results_comb.targets_analysed:
    -            if results_comb._single_target[target].omnibus_sign:
    -                pval = np.append(pval, results_comb._single_target[target].omnibus_pval)
    -                target_idx = np.append(target_idx, target)
    -                n_perm = np.append(n_perm, results_comb.settings.n_perm_omnibus)
    +            next_pval = results_comb._single_target[target].omnibus_pval
    +            pval = np.append(pval, next_pval if next_pval is not None else 1)
    +            target_idx = np.append(target_idx, target)
    +            n_perm = np.append(n_perm, results_comb.settings.n_perm_omnibus)
         else:  # individual variables
    +        # The total number of tests is the number of targets times the number
    +        # of source candidates (i.e. source processes * time lags) analyzed for each target
    +        n_tests = sum(
    +            len(results_comb._single_target[target].sources_tested)
    +            for target in results_comb.targets_analysed
    +        ) * (settings["max_lag_sources"] - settings["min_lag_sources"] + 1)
    +
             for target in results_comb.targets_analysed:
    -            if results_comb._single_target[target].omnibus_sign:
    -                n_sign = results_comb._single_target[target].selected_sources_pval.size
    -                pval = np.append(
    -                    pval, (results_comb._single_target[target].selected_sources_pval)
    -                )
    -                target_idx = np.append(target_idx, np.ones(n_sign) * target).astype(int)
    -                cands = cands + (
    -                    results_comb._single_target[target].selected_vars_sources
    -                )
    -                n_perm = np.append(n_perm, results_comb.settings.n_perm_max_seq)
    +            if results_comb._single_target[target].selected_sources_pval is None:
    +                continue
    +            n_sign = results_comb._single_target[target].selected_sources_pval.size
    +            pval = np.append(
    +                pval,
    +                [
    +                    next_pval if next_pval is not None else 1
    +                    for next_pval in results_comb._single_target[
    +                        target
    +                    ].selected_sources_pval
    +                ],
    +            )
    +            target_idx = np.append(target_idx, np.ones(n_sign) * target).astype(int)
    +            cands = cands + (results_comb._single_target[target].selected_vars_sources)
    +            n_perm = np.append(n_perm, results_comb.settings.n_perm_max_seq)
     
         if pval.size == 0:
             print("No links in final results ...")
    @@ -235,18 +251,14 @@ 

    Source code for idtxl.stats

             )
             return results_comb
     
    -    sign, thresh = _perform_fdr_corretion(pval, constant, alpha)
    +    sign, thresh = _perform_fdr_corretion(pval, constant, alpha, n_tests)
     
         # If the number of permutations for calculating p-values for individual
         # variables is too low, return without performing any correction.
         if (1 / min(n_perm)) > thresh[0]:
             print(
    -            "WARNING: Number of permutations ("
    -            "n_perm_max_seq"
    -            ") for at "
    -            "least one target is too low to allow for FDR correction "
    -            "(FDR-threshold: {0:.4f}, min. theoretically possible p-value: "
    -            "{1}).".format(thresh[0], 1 / min(n_perm))
    +            "WARNING: Number of permutations ('n_perm_max_seq') for at least one target is too low to allow for "
    +            f"FDR correction (FDR-threshold: {thresh[0]:.4f}, min. theoretically possible p-value: {1 / min(n_perm)})."
             )
             results_comb._add_fdr(
                 fdr=None,
    @@ -260,10 +272,9 @@ 

    Source code for idtxl.stats

         # the results object. Create a copy of the results object to leave the
         # original intact.
         fdr = cp.deepcopy(results_comb._single_target)
    -    for s in range(sign.shape[0]):
    -        if not sign[s]:
    +    for i, t in enumerate(target_idx):
    +        if not sign[i]:
                 if correct_by_target:
    -                t = target_idx[s]
                     fdr[t].selected_vars_full = cp.deepcopy(
                         results_comb._single_target[t].selected_vars_target
                     )
    @@ -273,22 +284,20 @@ 

    Source code for idtxl.stats

                     fdr[t].omnibus_pval = 1
                     fdr[t].omnibus_sign = False
                 else:
    -                t = target_idx[s]
    -                cand = cands[s]
    -                cand_ind = fdr[t].selected_vars_sources.index(cand)
    -                fdr[t].selected_vars_sources.pop(cand_ind)
    +                cand_ind = fdr[t].selected_vars_sources.index(cands[i])
    +                fdr[t].selected_vars_sources.remove(cands[i])
                     fdr[t].selected_sources_pval = np.delete(
                         fdr[t].selected_sources_pval, cand_ind
                     )
                     fdr[t].selected_sources_te = np.delete(
                         fdr[t].selected_sources_te, cand_ind
                     )
    -                fdr[t].selected_vars_full.pop(fdr[t].selected_vars_full.index(cand))
    +                fdr[t].selected_vars_full.remove(cands[i])
         results_comb._add_fdr(fdr, alpha, correct_by_target, constant)
         return results_comb
    -def _perform_fdr_corretion(pval, constant, alpha): +def _perform_fdr_corretion(pval, constant, alpha, n_tests): """Calculate sequential threshold for FDR-correction. Calculate sequential thresholds for FDR-correction of p-values. The @@ -306,43 +315,46 @@

    Source code for idtxl.stats

                 p-values to be corrected
             alpha : float
                 critical alpha level
    -        fdr_constant : int
    +        constant : int
                 one of two constants used for calculating the FDR-thresholds
                 according to Genovese (2002): 1 will divide alpha by 1, 2 will
                 divide alpha by the sum_i(1/i); see the paper for details on the
    -            assumptions (default=2)
    +            assumptions
    +        n_tests : int
    +            total number of tests performed for calculating the FDR-thresholds
     
         Returns:
             array of bools
    -            significance of p-values
    +            significance of p-values in the order of the input array
             array of floats
    -            FDR-thresholds for each p-value
    +            FDR-thresholds for each p-value in increasing order
         """
         # Sort all p-values in ascending order.
         sort_idx = np.argsort(pval)
    -    pval.sort()
    +    pval_sorted = np.sort(pval)
     
         # Calculate threshold
    -    n = pval.size
         if constant == 2:  # pick the requested constant (see Genovese, p.872)
    -        if n < 1000:
    -            const = sum(1 / np.arange(1, n + 1))
    +        if n_tests < 1000:
    +            const = np.sum(1 / np.arange(1, n_tests + 1))
             else:
    -            const = np.log(n) + np.e  # aprx. harmonic sum with Euler's number
    +            const = np.log(n_tests) + np.e  # aprx. harmonic sum with Euler's number
         elif constant == 1:
             # This is less strict than the other one and corresponds to a
             # Bonoferroni-correction for the first p-value, however, it makes more
             # strict assumptions on the distribution of p-values, while constant 2
             # works for any joint distribution of the p-values.
             const = 1
    -    thresh = (np.arange(1, n + 1) / n) * alpha / const
    +
    +    # Calculate threshold for each p-value.
    +    thresh = (np.arange(1, len(pval_sorted) + 1) / n_tests) * alpha / const
     
         # Compare data to threshold.
    -    sign = pval <= thresh
    +    sign = pval_sorted <= thresh
         if np.invert(sign).any():
             first_false = np.where(np.invert(sign))[0][0]
             sign[first_false:] = False  # avoids false positives due to equal pvals
    -    sign = sign[sort_idx]  # restore original ordering of significance values
    +    sign[sort_idx] = sign.copy()  # restore original ordering of significance values
         return sign, thresh
     
     
    @@ -377,7 +389,7 @@ 

    Source code for idtxl.stats

             float
                 the test's p-value
             float
    -            the estimated test statisic, i.e., the information transfer from
    +            the estimated test statistic, i.e., the information transfer from
                 all sources into the target
     
         Raises:
    @@ -546,13 +558,12 @@ 

    Source code for idtxl.stats

         calculation of surrogates for this statistic.
     
         Args:
    -
             analysis_setup : MultivariateTE instance
                 information on the current analysis, can have an optional attribute
    -            'settings', a dictionary with parameters for statistical testing:
    +            settings, a dictionary with parameters for statistical testing:
     
                 - n_perm_max_seq : int [optional] - number of permutations
    -              (default='n_perm_min_stat'|500)
    +              (default=n_perm_min_stat|500)
                 - alpha_max_seq : float [optional] - critical alpha level
                   (default=0.05)
                 - permute_in_time : bool [optional] - generate surrogates by
    @@ -580,8 +591,7 @@ 

    Source code for idtxl.stats

     
         if analysis_setup.settings["verbose"]:
             print(
    -            "sequential maximum statistic, n_perm: {0}, testing {1} selected"
    -            " sources".format(n_permutations, len(analysis_setup.selected_vars_sources))
    +            f"sequential maximum statistic, n_perm: {n_permutations}, testing {len(analysis_setup.selected_vars_sources)} selected sources"
             )
     
         assert analysis_setup.selected_vars_sources, "No sources to test."
    @@ -670,11 +680,9 @@ 

    Source code for idtxl.stats

                     #  we'll terminate the max sequential stats test,
                     #  and declare all not significant
                     print(
    -                    "AlgorithmExhaustedError encountered in estimations: {}.".format(
    -                        aee.message
    -                    )
    +                    f"AlgorithmExhaustedError encountered in estimations: {aee.message}. "
    +                    "Stopping sequential max stats at candidate with rank 0"
                     )
    -                print("Stopping sequential max stats at candidate with rank 0")
                     return (
                         np.zeros(len(analysis_setup.selected_vars_sources)).astype(bool),
                         np.ones(len(analysis_setup.selected_vars_sources)),
    @@ -695,11 +703,9 @@ 

    Source code for idtxl.stats

             #  we'll terminate the max sequential stats test,
             #  and declare all not significant
             print(
    -            "AlgorithmExhaustedError encountered in estimations: {}.".format(
    -                aee.message
    -            )
    +            f"AlgorithmExhaustedError encountered in estimations: {aee.message}. "
    +            "Stopping sequential max stats at candidate with rank 0"
             )
    -        print("Stopping sequential max stats at candidate with rank 0")
             # For now we don't need a stack trace:
             # traceback.print_tb(aee.__traceback__)
             # Return (signficance, pvalue, TEs):
    @@ -725,15 +731,12 @@ 

    Source code for idtxl.stats

             pvalue[c] = p
             if not s:  # break as soon as a candidate is no longer significant
                 if analysis_setup.settings["verbose"]:
    -                print(
    -                    "\nStopping sequential max stats at candidate with rank "
    -                    "{0}.".format(c)
    -                )
    +                print(f"\nStopping sequential max stats at candidate with rank {c}.")
                 break
     
         # Get back original order and return results.
    -    significance = significance[selected_vars_order]
    -    pvalue = pvalue[selected_vars_order]
    +    significance[selected_vars_order] = significance.copy()
    +    pvalue[selected_vars_order] = pvalue.copy()
         return significance, pvalue, individual_stat
    @@ -764,13 +767,12 @@

    Source code for idtxl.stats

         calculation of surrogates for this statistic.
     
         Args:
    -
             analysis_setup : MultivariateTE instance
                 information on the current analysis, can have an optional attribute
    -            'settings', a dictionary with parameters for statistical testing:
    +            settings, a dictionary with parameters for statistical testing:
     
                 - n_perm_max_seq : int [optional] - number of permutations
    -              (default='n_perm_min_stat'|500)
    +              (default=n_perm_min_stat|500)
                 - alpha_max_seq : float [optional] - critical alpha level
                   (default=0.05)
                 - permute_in_time : bool [optional] - generate surrogates by
    @@ -798,8 +800,7 @@ 

    Source code for idtxl.stats

     
         if analysis_setup.settings["verbose"]:
             print(
    -            "sequential maximum statistic, n_perm: {0}, testing {1} selected"
    -            " sources".format(n_permutations, len(analysis_setup.selected_vars_sources))
    +            f"sequential maximum statistic, n_perm: {n_permutations}, testing {len(analysis_setup.selected_vars_sources)} selected sources"
             )
     
         assert analysis_setup.selected_vars_sources, "No sources to test."
    @@ -821,12 +822,17 @@ 

    Source code for idtxl.stats

         )
         significance = np.zeros(len(analysis_setup.selected_vars_sources)).astype(bool)
         pvalue = np.ones(len(analysis_setup.selected_vars_sources))
    -    stat = np.zeros(len(analysis_setup.selected_vars_sources))
    +    individual_stat = np.zeros(len(analysis_setup.selected_vars_sources))
         for source in significant_sources:
             # Find selected past variables for current source
             source_vars = [
                 s for s in analysis_setup.selected_vars_sources if s[0] == source
             ]
    +        source_vars_idx = [
    +            i
    +            for i, s in enumerate(analysis_setup.selected_vars_sources)
    +            if s[0] == source
    +        ]
     
             # Determine length of conditioning set and allocate memory.
             idx_conditional = source_vars.copy()
    @@ -858,8 +864,8 @@ 

    Source code for idtxl.stats

                 temp_cand = data.get_realisations(
                     analysis_setup.current_value, [candidate]
                 )[0]
    -            # The following may happen if either the requested conditing is
    -            # 'none' or if the conditiong set that is tested consists only of
    +            # The following may happen if either the requested conditioning is
    +            # 'none' or if the conditioning set that is tested consists only of
                 # a single candidate.
                 if temp_cond is None:
                     conditional_realisations = conditional_realisations_target
    @@ -876,58 +882,60 @@ 

    Source code for idtxl.stats

                 i_1 = i_2
                 i_2 += data.n_realisations(analysis_setup.current_value)
     
    -        # Generate surrogates for the current candidate.
    -        if (
    -            analysis_setup._cmi_estimator.is_analytic_null_estimator()
    -            and permute_in_time
    -        ):
    -            # Generate the surrogates analytically
    -            surr_table[
    -                idx_c, :
    -            ] = analysis_setup._cmi_estimator.estimate_surrogates_analytic(
    -                n_perm=n_permutations,
    -                var1=data.get_realisations(analysis_setup.current_value, [candidate])[
    -                    0
    -                ],
    -                var2=analysis_setup._current_value_realisations,
    -                conditional=temp_cond,
    -            )
    -        else:
    -            analysis_setup.settings["analytical_surrogates"] = False
    -            surr_candidate_realisations = _get_surrogates(
    -                data,
    -                analysis_setup.current_value,
    -                [candidate],
    -                n_permutations,
    -                analysis_setup.settings,
    -            )
    -            try:
    -                surr_table[idx_c, :] = analysis_setup._cmi_estimator.estimate_parallel(
    -                    n_chunks=n_permutations,
    -                    re_use=["var2", "conditional"],
    -                    var1=surr_candidate_realisations,
    +            # Generate surrogates for the current candidate.
    +            if (
    +                analysis_setup._cmi_estimator.is_analytic_null_estimator()
    +                and permute_in_time
    +            ):
    +                # Generate the surrogates analytically
    +                surr_table[
    +                    idx_c, :
    +                ] = analysis_setup._cmi_estimator.estimate_surrogates_analytic(
    +                    n_perm=n_permutations,
    +                    var1=data.get_realisations(
    +                        analysis_setup.current_value, [candidate]
    +                    )[0],
                         var2=analysis_setup._current_value_realisations,
                         conditional=temp_cond,
                     )
    -            except ex.AlgorithmExhaustedError as aee:
    -                # The aglorithm cannot continue here, so
    -                #  we'll terminate the max sequential stats test,
    -                #  and declare all not significant
    -                print(
    -                    "AlgorithmExhaustedError encountered in estimations: {}.".format(
    -                        aee.message
    -                    )
    -                )
    -                print("Stopping sequential max stats at candidate with rank 0")
    -                return (
    -                    np.zeros(len(analysis_setup.selected_vars_sources)).astype(bool),
    -                    np.ones(len(analysis_setup.selected_vars_sources)),
    -                    np.zeros(len(analysis_setup.selected_vars_sources)),
    +            else:
    +                analysis_setup.settings["analytical_surrogates"] = False
    +                surr_candidate_realisations = _get_surrogates(
    +                    data,
    +                    analysis_setup.current_value,
    +                    [candidate],
    +                    n_permutations,
    +                    analysis_setup.settings,
                     )
    +                try:
    +                    surr_table[
    +                        idx_c, :
    +                    ] = analysis_setup._cmi_estimator.estimate_parallel(
    +                        n_chunks=n_permutations,
    +                        re_use=["var2", "conditional"],
    +                        var1=surr_candidate_realisations,
    +                        var2=analysis_setup._current_value_realisations,
    +                        conditional=temp_cond,
    +                    )
    +                except ex.AlgorithmExhaustedError as aee:
    +                    # The aglorithm cannot continue here, so
    +                    #  we'll terminate the max sequential stats test,
    +                    #  and declare all not significant
    +                    print(
    +                        f"AlgorithmExhaustedError encountered in estimations: {aee.message}. "
    +                        "Stopping sequential max stats at candidate with rank 0"
    +                    )
    +                    return (
    +                        np.zeros(len(analysis_setup.selected_vars_sources)).astype(
    +                            bool
    +                        ),
    +                        np.ones(len(analysis_setup.selected_vars_sources)),
    +                        np.zeros(len(analysis_setup.selected_vars_sources)),
    +                    )
     
    -        # Calculate original statistic (multivariate/bivariate TE/MI)
    +        # Calculate original statistic (bivariate TE/MI)
             try:
    -            individual_stat = analysis_setup._cmi_estimator.estimate_parallel(
    +            individual_stat_source = analysis_setup._cmi_estimator.estimate_parallel(
                     n_chunks=len(source_vars),
                     re_use=re_use,
                     var1=candidate_realisations,
    @@ -939,10 +947,9 @@ 

    Source code for idtxl.stats

                 #  we'll terminate the max sequential stats test,
                 #  and declare all not significant
                 print(
    -                "AlgorithmExhaustedError encountered in "
    -                "estimations: {}.".format(aee.message)
    +                f"AlgorithmExhaustedError encountered in estimations: {aee.message}. "
    +                "Stopping sequential max stats at candidate with rank 0"
                 )
    -            print("Stopping sequential max stats at candidate with rank 0")
                 # For now we don't need a stack trace:
                 # traceback.print_tb(aee.__traceback__)
                 # Return (signficance, pvalue, TEs):
    @@ -952,38 +959,40 @@ 

    Source code for idtxl.stats

                     np.zeros(len(analysis_setup.selected_vars_sources)),
                 )
     
    -        selected_vars_order = utils.argsort_descending(individual_stat)
    -        individual_stat_sorted = utils.sort_descending(individual_stat)
    +        selected_vars_order = utils.argsort_descending(individual_stat_source)
    +        individual_stat_source_sorted = utils.sort_descending(individual_stat_source)
             max_distribution = _sort_table_max(surr_table)
    +        significance_source = np.zeros(individual_stat_source.shape[0], dtype=bool)
    +        pvalue_source = np.ones(individual_stat_source.shape[0])
     
             # Compare each original value with the distribution of the same rank,
             # starting with the highest value.
    -        for c in range(individual_stat.shape[0]):
    -            [s, p] = _find_pvalue(
    -                individual_stat_sorted[c],
    +        for c in range(individual_stat_source.shape[0]):
    +            s, p = _find_pvalue(
    +                individual_stat_source_sorted[c],
                     max_distribution[c,],
                     alpha,
                     tail="one_bigger",
                 )
    -            # Write results into an array with the same order as the set of
    -            # selected sources from all process. Find the currently tested
    -            # variable and its index in the list of all selected variables.
    -            current_var = source_vars[selected_vars_order[c]]
    -            for ind, v in enumerate(analysis_setup.selected_vars_sources):
    -                if v == current_var:
    -                    break
    -            significance[ind] = s
    -            pvalue[ind] = p
    -            stat[ind] = individual_stat_sorted[c]
    +            significance_source[c] = s
    +            pvalue_source[c] = p
    +
                 if not s:  # break as soon as a candidate is no longer significant
                     if analysis_setup.settings["verbose"]:
                         print(
    -                        "\nStopping sequential max stats at candidate with "
    -                        "rank {0}.".format(c)
    +                        f"\nStopping sequential max stats at candidate with rank {c}."
                         )
                     break
     
    -    return significance, pvalue, stat
    + # Get back original order of variables within the current source. Write + # re-ordered results into global results array at the respective + # variable locations. + significance_source[selected_vars_order] = significance_source.copy() + pvalue_source[selected_vars_order] = pvalue_source.copy() + significance[source_vars_idx] = significance_source + pvalue[source_vars_idx] = pvalue_source + individual_stat[source_vars_idx] = individual_stat_source + return significance, pvalue, individual_stat
    [docs]def min_statistic( @@ -1102,38 +1111,9 @@

    Source code for idtxl.stats

         permute_in_time = _check_permute_in_time(analysis_setup, data, n_perm)
         if analysis_setup.settings["verbose"]:
             print(
    -            "mi permutation test against surrogates, n_perm: {0}".format(
    -                analysis_setup.settings["n_perm_mi"]
    -            )
    +            f"mi permutation test against surrogates, n_perm: {analysis_setup.settings['n_perm_mi']}"
             )
    -    """
    -    surr_realisations = np.empty(
    -                        (data.n_realisations(analysis_setup.current_value) *
    -                         (n_perm + 1), 1))
    -    i_1 = 0
    -    i_2 = data.n_realisations(analysis_setup.current_value)
    -    # The first chunk holds the original data
    -    surr_realisations[i_1:i_2, ] = analysis_setup._current_value_realisations
    -    # Create surrogate data by shuffling the realisations of the current value.
    -    for perm in range(n_perm):
    -        i_1 = i_2
    -        i_2 += data.n_realisations(analysis_setup.current_value)
    -        # Check the permutation type for the current candidate.
    -        if permute_over_replications:
    -            surr_temp = data.permute_data(analysis_setup.current_value,
    -                                          [analysis_setup.current_value])[0]
    -        else:
    -            [real, repl_idx] = data.get_realisations(
    -                                            analysis_setup.current_value,
    -                                            [analysis_setup.current_value])
    -            surr_temp = _permute_realisations(real, repl_idx, perm_range)
    -        # Add current shuffled realisation to the array of all realisations for
    -        # parallel MI estimation.
    -        # surr_realisations[i_1:i_2, ] = surr_temp
    -        [real, repl_idx] = data.get_realisations(
    -                                            analysis_setup.current_value,
    -                                            [analysis_setup.current_value])
    -        """
    +
         if analysis_setup._cmi_estimator.is_analytic_null_estimator() and permute_in_time:
             # Generate the surrogates analytically
             analysis_setup.settings["analytical_surrogates"] = True
    @@ -1557,7 +1537,7 @@ 

    Source code for idtxl.stats

         assert distribution.ndim == 1, "Test distribution must be 1D."
         check_n_perm(distribution.shape[0], alpha)
     
    -    if tail == "one_bigger" or tail == "one":
    +    if tail in ["one_bigger", "one"]:
             pvalue = sum(distribution >= statistic) / distribution.shape[0]
         elif tail == "one_smaller":
             pvalue = sum(distribution <= statistic) / distribution.shape[0]
    @@ -1568,20 +1548,7 @@ 

    Source code for idtxl.stats

             alpha = alpha / 2
         else:
             raise ValueError(
    -            (
    -                "Unkown value for "
    -                "tail"
    -                ", should be "
    -                "one"
    -                ", "
    -                "one_bigger"
    -                ","
    -                " "
    -                "one_smaller"
    -                ", or "
    -                "two"
    -                "): {0}.".format(tail)
    -            )
    +            f"Unkown value for tail: {tail}, should be one, one_bigger, one_smaller, or two"
             )
     
         # If the statistic is larger than all values in the test distribution, set
    @@ -1777,7 +1744,7 @@ 

    Navigation

  • modules |
  • - + diff --git a/docs/html/_modules/idtxl/visualise_graph.html b/docs/html/_modules/idtxl/visualise_graph.html index 118204c..f046873 100644 --- a/docs/html/_modules/idtxl/visualise_graph.html +++ b/docs/html/_modules/idtxl/visualise_graph.html @@ -5,7 +5,7 @@ - idtxl.visualise_graph — IDTxl 1.5.1 documentation + idtxl.visualise_graph — IDTxl 1.6.0 documentation @@ -31,7 +31,7 @@

    Navigation

  • modules |
  • - + @@ -401,7 +401,7 @@

    Navigation

  • modules |
  • - + diff --git a/docs/html/_modules/index.html b/docs/html/_modules/index.html index c7c6937..d735a55 100644 --- a/docs/html/_modules/index.html +++ b/docs/html/_modules/index.html @@ -5,7 +5,7 @@ - Overview: module code — IDTxl 1.5.1 documentation + Overview: module code — IDTxl 1.6.0 documentation @@ -31,7 +31,7 @@

    Navigation

  • modules |
  • - +
    @@ -100,7 +100,7 @@

    Navigation

  • modules |
  • - +
    diff --git a/docs/html/_static/documentation_options.js b/docs/html/_static/documentation_options.js index eb42507..50f8de0 100644 --- a/docs/html/_static/documentation_options.js +++ b/docs/html/_static/documentation_options.js @@ -1,6 +1,6 @@ var DOCUMENTATION_OPTIONS = { URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'), - VERSION: '1.5.1', + VERSION: '1.6.0', LANGUAGE: 'None', COLLAPSE_INDEX: false, BUILDER: 'html', diff --git a/docs/html/genindex.html b/docs/html/genindex.html index 5f30632..72024e7 100644 --- a/docs/html/genindex.html +++ b/docs/html/genindex.html @@ -5,7 +5,7 @@ - Index — IDTxl 1.5.1 documentation + Index — IDTxl 1.6.0 documentation @@ -31,7 +31,7 @@

    Navigation

  • modules |
  • - +
    @@ -1058,7 +1058,7 @@

    Navigation

  • modules |
  • - +
    diff --git a/docs/html/idtxl.html b/docs/html/idtxl.html index c3a7a26..22f704a 100644 --- a/docs/html/idtxl.html +++ b/docs/html/idtxl.html @@ -6,7 +6,7 @@ - idtxl package — IDTxl 1.5.1 documentation + idtxl package — IDTxl 1.6.0 documentation @@ -36,7 +36,7 @@

    Navigation

  • previous |
  • - +
    @@ -5212,7 +5212,8 @@

    Submodules
    estimate(s, t)[source]¶
    -
    +

    Estimate SxPID from list of sources and a target

    +
    Args:
    slist of numpy arrays

    1D arrays containing realizations of a discrete random variable

    @@ -5221,18 +5222,16 @@

    Submodules +
    SxPID results, with entries
      +
    • ‘ptw’ -> { realization -> {alpha -> [float, float, float]}}: pointwise decomposition

    • +
    • ‘avg’ -> {alpha -> [float, float, float]}: average decomposition

    • +

    -

    }

    +

    the list of floats is ordered [informative, misinformative, informative - misinformative]

    -

    where the list of floats is ordered -[informative, misinformative, informative - misinformative] -ptw stands for pointwise decomposition -avg stands for average decomposition

    @@ -5971,12 +5970,16 @@

    Submodules class idtxl.idtxl_utils.timeout(timeout_duration, exception_message='Timeout')[source]¶

    Bases: object

    -

    Context manager for a timeout using threading module. -args:

    -
    -

    timeout_duration: float, number of seconds to wait before timeout is triggered -exception_message: string, message to put in the exception

    -
    +

    Context manager for a timeout using threading module.

    +
    +
    Args:
    +
    timeout_duration: float

    number of seconds to wait before timeout is triggered

    +
    +
    exception_messagestring

    message to put in the exception

    +
    +
    +
    +
    timeout_handler()[source]¶
    @@ -6006,7 +6009,7 @@

    Submodules
    Args:
    -
    file_pathstr

    path to checkpoint file (excluding extension: *.ckp)

    +
    file_pathstr

    path to checkpoint file (excluding extension: .ckp)

    @@ -7177,44 +7180,87 @@

    Submodules +
      +
    • Processint

      Process that was optimized

      +
      +
    • +
    • estimation_methodString

      Estimation method that was used for optimization

      +
      +
    • +
    • T_Dfloat

      Estimated optimal value for the temporal depth TD

      +
      +
    • +
    • tau_R :

      Information timescale tau_R, a characteristic timescale of history dependence similar to an autocorrelation time.

      +
      +
    • +
    • R_totfloat

      Estimated value for the total history dependence Rtot,

      +
      +
    • +
    • AIS_totfloat

      Estimated value for the total active information storage

      +
      +
    • +
    • opt_number_of_bins_dint

      Number of bins d for the embedding that yields (RÌ‚tot ,TÌ‚D)

      +
      +
    • +
    • opt_scaling_kint

      Scaling exponent κ for the embedding that yields (R̂tot , T̂D)

      +
      +
    • +
    • opt_first_bin_sizeint

      Size of the first bin Ï„1 for the embedding that yields (RÌ‚tot , TÌ‚D ),

      +
      +
    • +
    • history_dependencearray with floating-point values

      Estimated history dependence for each embedding

      +
      +
    • +
    • firing_ratefloat

      Firing rate of the neuron/ spike train

      +
      +
    • +
    • recording_lengthfloat

      Length of the recording (in seconds)

      +
      +
    • +
    • H_spikingfloat

      Entropy of the spike times

      -

      if analyse_auto_MI was set to True additionally: -auto_MI : dict

      -
      -

      numpy array of MI values for each delay

      -
      -
      +
    • +
    +

    if analyse_auto_MI was set to True additionally:

    +
      +
    • +
      auto_MIdict

      numpy array of MI values for each delay

      +
      +
      +
    • +
    • auto_MI_delayslist of int

      list of delays depending on the given auto_MI_bin_sizes and auto_MI_max_delay

      +
    • +

    @@ -7360,14 +7406,13 @@

    Submodules +
    +
    Args:
    analysis_setupMultivariateTE instance

    information on the current analysis, can have an optional attribute -‘settings’, a dictionary with parameters for statistical testing:

    +settings, a dictionary with parameters for statistical testing:

    -

    -
    +
    Returns:
    numpy array, bool

    statistical significance of each source

    @@ -7416,14 +7460,13 @@

    Submodules +
    +
    Args:
    analysis_setupMultivariateTE instance

    information on the current analysis, can have an optional attribute -‘settings’, a dictionary with parameters for statistical testing:

    +settings, a dictionary with parameters for statistical testing:

    -

    -
    +
    Returns:
    numpy array, bool

    statistical significance of each source

    @@ -7618,7 +7660,7 @@

    Submodules previous | - +

    diff --git a/docs/html/idtxl_data_class.html b/docs/html/idtxl_data_class.html index 1dd6c25..8bcb9ce 100644 --- a/docs/html/idtxl_data_class.html +++ b/docs/html/idtxl_data_class.html @@ -6,7 +6,7 @@ - The Data Class — IDTxl 1.5.1 documentation + The Data Class — IDTxl 1.6.0 documentation @@ -40,7 +40,7 @@

    Navigation

  • previous |
  • - +
    @@ -939,7 +939,7 @@

    Navigation

  • previous |
  • - +
    diff --git a/docs/html/idtxl_estimators.html b/docs/html/idtxl_estimators.html index 1eb6c1e..901e010 100644 --- a/docs/html/idtxl_estimators.html +++ b/docs/html/idtxl_estimators.html @@ -6,7 +6,7 @@ - Information theoretic estimators — IDTxl 1.5.1 documentation + Information theoretic estimators — IDTxl 1.6.0 documentation @@ -40,7 +40,7 @@

    Navigation

  • previous |
  • - +
    @@ -1937,7 +1937,7 @@

    Navigation

  • previous |
  • - +
    diff --git a/docs/html/idtxl_helper.html b/docs/html/idtxl_helper.html index a4bb4b2..6779bcc 100644 --- a/docs/html/idtxl_helper.html +++ b/docs/html/idtxl_helper.html @@ -6,7 +6,7 @@ - Helper functions — IDTxl 1.5.1 documentation + Helper functions — IDTxl 1.6.0 documentation @@ -40,7 +40,7 @@

    Navigation

  • previous |
  • - +
    @@ -281,12 +281,16 @@

    utils module
    class idtxl.idtxl_utils.timeout(timeout_duration, exception_message='Timeout')[source]
    -

    Context manager for a timeout using threading module. -args:

    -
    -

    timeout_duration: float, number of seconds to wait before timeout is triggered -exception_message: string, message to put in the exception

    -
    +

    Context manager for a timeout using threading module.

    +
    +
    Args:
    +
    timeout_duration: float

    number of seconds to wait before timeout is triggered

    +
    +
    exception_messagestring

    message to put in the exception

    +
    +
    +
    +
    @@ -420,14 +424,13 @@

    stats module +
    +
    Args:
    analysis_setupMultivariateTE instance

    information on the current analysis, can have an optional attribute -‘settings’, a dictionary with parameters for statistical testing:

    +settings, a dictionary with parameters for statistical testing:

    -

    -
    +
    Returns:
    numpy array, bool

    statistical significance of each source

    @@ -476,14 +478,13 @@

    stats module +
    +
    Args:
    analysis_setupMultivariateTE instance

    information on the current analysis, can have an optional attribute -‘settings’, a dictionary with parameters for statistical testing:

    +settings, a dictionary with parameters for statistical testing:

    -

    -
    +
    Returns:
    numpy array, bool

    statistical significance of each source

    @@ -678,7 +678,7 @@

    stats module previous | - +

    diff --git a/docs/html/idtxl_network_comparison.html b/docs/html/idtxl_network_comparison.html index 584408a..0f8b02f 100644 --- a/docs/html/idtxl_network_comparison.html +++ b/docs/html/idtxl_network_comparison.html @@ -6,7 +6,7 @@ - Network comparison — IDTxl 1.5.1 documentation + Network comparison — IDTxl 1.6.0 documentation @@ -40,7 +40,7 @@

    Navigation

  • previous |
  • - +
    @@ -319,7 +319,7 @@

    Navigation

  • previous |
  • - +
    diff --git a/docs/html/idtxl_network_inference.html b/docs/html/idtxl_network_inference.html index be6534c..164d9ea 100644 --- a/docs/html/idtxl_network_inference.html +++ b/docs/html/idtxl_network_inference.html @@ -6,7 +6,7 @@ - Algorithms for network inference — IDTxl 1.5.1 documentation + Algorithms for network inference — IDTxl 1.6.0 documentation @@ -40,7 +40,7 @@

    Navigation

  • previous |
  • - +
    @@ -1056,7 +1056,7 @@

    Navigation

  • previous |
  • - +
    diff --git a/docs/html/idtxl_postprocessing.html b/docs/html/idtxl_postprocessing.html index e5f536e..74b6ff4 100644 --- a/docs/html/idtxl_postprocessing.html +++ b/docs/html/idtxl_postprocessing.html @@ -6,7 +6,7 @@ - Postprocessing of inferred networks — IDTxl 1.5.1 documentation + Postprocessing of inferred networks — IDTxl 1.6.0 documentation @@ -40,7 +40,7 @@

    Navigation

  • previous |
  • - +
    @@ -657,7 +657,7 @@

    Navigation

  • previous |
  • - +
    diff --git a/docs/html/idtxl_process_analysis.html b/docs/html/idtxl_process_analysis.html index 7b2c9f1..116a009 100644 --- a/docs/html/idtxl_process_analysis.html +++ b/docs/html/idtxl_process_analysis.html @@ -6,7 +6,7 @@ - Algorithms for the analysis of node dynamics — IDTxl 1.5.1 documentation + Algorithms for the analysis of node dynamics — IDTxl 1.6.0 documentation @@ -40,7 +40,7 @@

    Navigation

  • previous |
  • - +
    @@ -1753,7 +1753,7 @@

    Navigation

  • previous |
  • - +
    diff --git a/docs/html/idtxl_results_class.html b/docs/html/idtxl_results_class.html index b1c9e08..9208b19 100644 --- a/docs/html/idtxl_results_class.html +++ b/docs/html/idtxl_results_class.html @@ -6,7 +6,7 @@ - The Results Class — IDTxl 1.5.1 documentation + The Results Class — IDTxl 1.6.0 documentation @@ -40,7 +40,7 @@

    Navigation

  • previous |
  • - +
    @@ -705,44 +705,87 @@

    Results node dynamics -
    +
      +
    • Processint

      Process that was optimized

      +
      +
    • +
    • estimation_methodString

      Estimation method that was used for optimization

      +
      +
    • +
    • T_Dfloat

      Estimated optimal value for the temporal depth TD

      +
      +
    • +
    • tau_R :

      Information timescale tau_R, a characteristic timescale of history dependence similar to an autocorrelation time.

      +
      +
    • +
    • R_totfloat

      Estimated value for the total history dependence Rtot,

      +
      +
    • +
    • AIS_totfloat

      Estimated value for the total active information storage

      +
      +
    • +
    • opt_number_of_bins_dint

      Number of bins d for the embedding that yields (RÌ‚tot ,TÌ‚D)

      +
      +
    • +
    • opt_scaling_kint

      Scaling exponent κ for the embedding that yields (R̂tot , T̂D)

      +
      +
    • +
    • opt_first_bin_sizeint

      Size of the first bin Ï„1 for the embedding that yields (RÌ‚tot , TÌ‚D ),

      +
      +
    • +
    • history_dependencearray with floating-point values

      Estimated history dependence for each embedding

      +
      +
    • +
    • firing_ratefloat

      Firing rate of the neuron/ spike train

      +
      +
    • +
    • recording_lengthfloat

      Length of the recording (in seconds)

      +
      +
    • +
    • H_spikingfloat

      Entropy of the spike times

      -

      if analyse_auto_MI was set to True additionally: -auto_MI : dict

      -
      -

      numpy array of MI values for each delay

      -
      -
      +
    • +
    +

    if analyse_auto_MI was set to True additionally:

    +
      +
    • +
      auto_MIdict

      numpy array of MI values for each delay

      +
      +
      +
    • +
    • auto_MI_delayslist of int

      list of delays depending on the given auto_MI_bin_sizes and auto_MI_max_delay

      +
    • +
    @@ -825,7 +868,7 @@

    Navigation

  • previous |
  • - +

    diff --git a/docs/html/index.html b/docs/html/index.html index c491a3c..ffaac49 100644 --- a/docs/html/index.html +++ b/docs/html/index.html @@ -6,7 +6,7 @@ - Welcome to IDTxl’s documentation! — IDTxl 1.5.1 documentation + Welcome to IDTxl’s documentation! — IDTxl 1.6.0 documentation @@ -36,7 +36,7 @@

    Navigation

  • next |
  • - +
    @@ -206,7 +206,7 @@

    Navigation

  • next |
  • - +
    diff --git a/docs/html/modules.html b/docs/html/modules.html index f258837..2fb3f73 100644 --- a/docs/html/modules.html +++ b/docs/html/modules.html @@ -6,7 +6,7 @@ - idtxl — IDTxl 1.5.1 documentation + idtxl — IDTxl 1.6.0 documentation @@ -32,7 +32,7 @@

    Navigation

  • modules |
  • - +
    @@ -118,7 +118,7 @@

    Navigation

  • modules |
  • - +
    diff --git a/docs/html/objects.inv b/docs/html/objects.inv index 351572d735a798d611a9076349e00603b01a4acb..3df3f28c5fe2155bfe4749d860dd0606868ba19a 100644 GIT binary patch delta 14 VcmX>pdQxpdQx - Python Module Index — IDTxl 1.5.1 documentation + Python Module Index — IDTxl 1.6.0 documentation @@ -34,7 +34,7 @@

    Navigation

  • modules |
  • - +
    @@ -223,7 +223,7 @@

    Navigation

  • modules |
  • - +
    diff --git a/docs/html/search.html b/docs/html/search.html index 0f9bfc4..bee715a 100644 --- a/docs/html/search.html +++ b/docs/html/search.html @@ -5,7 +5,7 @@ - Search — IDTxl 1.5.1 documentation + Search — IDTxl 1.6.0 documentation @@ -37,7 +37,7 @@

    Navigation

  • modules |
  • - +
    @@ -97,7 +97,7 @@

    Navigation

  • modules |
  • - +
    diff --git a/docs/html/searchindex.js b/docs/html/searchindex.js index 0893c9b..e57c9ab 100644 --- a/docs/html/searchindex.js +++ b/docs/html/searchindex.js @@ -1 +1 @@ -Search.setIndex({docnames:["idtxl","idtxl_data_class","idtxl_estimators","idtxl_helper","idtxl_network_comparison","idtxl_network_inference","idtxl_postprocessing","idtxl_process_analysis","idtxl_results_class","index","modules"],envversion:{"sphinx.domains.c":2,"sphinx.domains.changeset":1,"sphinx.domains.citation":1,"sphinx.domains.cpp":4,"sphinx.domains.index":1,"sphinx.domains.javascript":2,"sphinx.domains.math":2,"sphinx.domains.python":3,"sphinx.domains.rst":2,"sphinx.domains.std":2,"sphinx.ext.viewcode":1,sphinx:56},filenames:["idtxl.rst","idtxl_data_class.rst","idtxl_estimators.rst","idtxl_helper.rst","idtxl_network_comparison.rst","idtxl_network_inference.rst","idtxl_postprocessing.rst","idtxl_process_analysis.rst","idtxl_results_class.rst","index.rst","modules.rst"],objects:{"":[[0,0,0,"-","idtxl"]],"idtxl.active_information_storage":[[0,1,1,"","ActiveInformationStorage"]],"idtxl.active_information_storage.ActiveInformationStorage":[[0,2,1,"","analyse_network"],[0,2,1,"","analyse_single_process"]],"idtxl.bivariate_mi":[[0,1,1,"","BivariateMI"]],"idtxl.bivariate_mi.BivariateMI":[[0,2,1,"","analyse_network"],[0,2,1,"","analyse_single_target"]],"idtxl.bivariate_pid":[[0,1,1,"","BivariatePID"]],"idtxl.bivariate_pid.BivariatePID":[[0,2,1,"","analyse_network"],[0,2,1,"","analyse_single_target"]],"idtxl.bivariate_te":[[0,1,1,"","BivariateTE"]],"idtxl.bivariate_te.BivariateTE":[[0,2,1,"","analyse_network"],[0,2,1,"","analyse_single_target"]],"idtxl.data":[[0,1,1,"","Data"]],"idtxl.data.Data":[[0,3,1,"","data"],[0,2,1,"","generate_logistic_maps_data"],[0,2,1,"","generate_mute_data"],[0,2,1,"","generate_var_data"],[0,2,1,"","get_realisations"],[0,2,1,"","get_seed"],[0,2,1,"","get_state"],[0,2,1,"","n_realisations"],[0,2,1,"","n_realisations_repl"],[0,2,1,"","n_realisations_samples"],[0,2,1,"","permute_replications"],[0,2,1,"","permute_samples"],[0,2,1,"","set_data"],[0,2,1,"","slice_permute_replications"],[0,2,1,"","slice_permute_samples"]],"idtxl.embedding_optimization_ais_Rudelt":[[0,1,1,"","OptimizationRudelt"]],"idtxl.embedding_optimization_ais_Rudelt.OptimizationRudelt":[[0,2,1,"","analyse_auto_MI"],[0,2,1,"","check_inputs"],[0,2,1,"","compute_CIs"],[0,2,1,"","get_R_tot"],[0,2,1,"","get_auto_MI"],[0,2,1,"","get_bootstrap_history_dependence"],[0,2,1,"","get_embeddings"],[0,2,1,"","get_embeddings_that_maximise_R"],[0,2,1,"","get_history_dependence"],[0,2,1,"","get_information_timescale_tau_R"],[0,2,1,"","get_past_range"],[0,2,1,"","get_set_of_scalings"],[0,2,1,"","get_temporal_depth_T_D"],[0,2,1,"","optimize"],[0,2,1,"","optimize_single_run"],[0,2,1,"","remove_subresults_single_process"]],"idtxl.estimators_Rudelt":[[0,1,1,"","RudeltAbstractEstimator"],[0,1,1,"","RudeltAbstractNSBEstimator"],[0,1,1,"","RudeltBBCEstimator"],[0,1,1,"","RudeltNSBEstimatorSymbolsMI"],[0,1,1,"","RudeltPluginEstimatorSymbolsMI"],[0,1,1,"","RudeltShufflingEstimator"]],"idtxl.estimators_Rudelt.RudeltAbstractEstimator":[[0,2,1,"","get_median_number_of_spikes_per_bin"],[0,2,1,"","get_multiplicities"],[0,2,1,"","get_past_range"],[0,2,1,"","get_raw_symbols"],[0,2,1,"","get_symbol_counts"],[0,2,1,"","get_window_delimiters"],[0,2,1,"","is_analytic_null_estimator"],[0,2,1,"","is_parallel"],[0,2,1,"","symbol_array_to_binary"],[0,2,1,"","symbol_binary_to_array"]],"idtxl.estimators_Rudelt.RudeltAbstractNSBEstimator":[[0,2,1,"","H1"],[0,2,1,"","alpha_ML"],[0,2,1,"","d2_log_rho"],[0,2,1,"","d2_log_rho_xi"],[0,2,1,"","d2_xi"],[0,2,1,"","d3_xi"],[0,2,1,"","d_log_rho"],[0,2,1,"","d_log_rho_xi"],[0,2,1,"","d_xi"],[0,2,1,"","get_beta_MAP"],[0,2,1,"","get_integration_bounds"],[0,2,1,"","log_likelihood_DP_alpha"],[0,2,1,"","nsb_entropy"],[0,2,1,"","rho"],[0,2,1,"","unnormalized_posterior"]],"idtxl.estimators_Rudelt.RudeltBBCEstimator":[[0,2,1,"","bayesian_bias_criterion"],[0,2,1,"","estimate"],[0,2,1,"","get_bbc_term"]],"idtxl.estimators_Rudelt.RudeltNSBEstimatorSymbolsMI":[[0,2,1,"","estimate"],[0,2,1,"","nsb_estimator"]],"idtxl.estimators_Rudelt.RudeltPluginEstimatorSymbolsMI":[[0,2,1,"","estimate"],[0,2,1,"","plugin_entropy"],[0,2,1,"","plugin_estimator"]],"idtxl.estimators_Rudelt.RudeltShufflingEstimator":[[0,2,1,"","estimate"],[0,2,1,"","get_H0_X_past_cond_X"],[0,2,1,"","get_H0_X_past_cond_X_eq_x"],[0,2,1,"","get_H_X_past_cond_X"],[0,2,1,"","get_H_X_past_uncond"],[0,2,1,"","get_P_X_past_cond_X"],[0,2,1,"","get_P_X_past_uncond"],[0,2,1,"","get_P_X_uncond"],[0,2,1,"","get_marginal_frequencies_of_spikes_in_bins"],[0,2,1,"","get_shuffled_symbol_counts"],[0,2,1,"","shuffling_MI"]],"idtxl.estimators_jidt":[[0,1,1,"","JidtDiscrete"],[0,1,1,"","JidtDiscreteAIS"],[0,1,1,"","JidtDiscreteCMI"],[0,1,1,"","JidtDiscreteMI"],[0,1,1,"","JidtDiscreteTE"],[0,1,1,"","JidtEstimator"],[0,1,1,"","JidtGaussian"],[0,1,1,"","JidtGaussianAIS"],[0,1,1,"","JidtGaussianCMI"],[0,1,1,"","JidtGaussianMI"],[0,1,1,"","JidtGaussianTE"],[0,1,1,"","JidtKraskov"],[0,1,1,"","JidtKraskovAIS"],[0,1,1,"","JidtKraskovCMI"],[0,1,1,"","JidtKraskovMI"],[0,1,1,"","JidtKraskovTE"],[0,4,1,"","common_estimate_surrogates_analytic"]],"idtxl.estimators_jidt.JidtDiscrete":[[0,2,1,"","estimate_surrogates_analytic"],[0,2,1,"","get_analytic_distribution"],[0,2,1,"","is_analytic_null_estimator"]],"idtxl.estimators_jidt.JidtDiscreteAIS":[[0,2,1,"","estimate"],[0,2,1,"","get_analytic_distribution"]],"idtxl.estimators_jidt.JidtDiscreteCMI":[[0,2,1,"","estimate"],[0,2,1,"","get_analytic_distribution"]],"idtxl.estimators_jidt.JidtDiscreteMI":[[0,2,1,"","estimate"],[0,2,1,"","get_analytic_distribution"]],"idtxl.estimators_jidt.JidtDiscreteTE":[[0,2,1,"","estimate"],[0,2,1,"","get_analytic_distribution"]],"idtxl.estimators_jidt.JidtEstimator":[[0,2,1,"","is_parallel"]],"idtxl.estimators_jidt.JidtGaussian":[[0,2,1,"","estimate_surrogates_analytic"],[0,2,1,"","get_analytic_distribution"],[0,2,1,"","is_analytic_null_estimator"]],"idtxl.estimators_jidt.JidtGaussianAIS":[[0,2,1,"","estimate"]],"idtxl.estimators_jidt.JidtGaussianCMI":[[0,2,1,"","estimate"],[0,2,1,"","get_analytic_distribution"]],"idtxl.estimators_jidt.JidtGaussianMI":[[0,2,1,"","estimate"]],"idtxl.estimators_jidt.JidtGaussianTE":[[0,2,1,"","estimate"]],"idtxl.estimators_jidt.JidtKraskov":[[0,2,1,"","is_analytic_null_estimator"]],"idtxl.estimators_jidt.JidtKraskovAIS":[[0,2,1,"","estimate"]],"idtxl.estimators_jidt.JidtKraskovCMI":[[0,2,1,"","estimate"]],"idtxl.estimators_jidt.JidtKraskovMI":[[0,2,1,"","estimate"]],"idtxl.estimators_jidt.JidtKraskovTE":[[0,2,1,"","estimate"]],"idtxl.estimators_mpi":[[0,1,1,"","MPIEstimator"]],"idtxl.estimators_mpi.MPIEstimator":[[0,2,1,"","estimate"],[0,2,1,"","estimate_surrogates_analytic"],[0,2,1,"","is_analytic_null_estimator"],[0,2,1,"","is_parallel"]],"idtxl.estimators_multivariate_pid":[[0,1,1,"","SxPID"]],"idtxl.estimators_multivariate_pid.SxPID":[[0,2,1,"","estimate"],[0,2,1,"","is_analytic_null_estimator"],[0,2,1,"","is_parallel"]],"idtxl.estimators_opencl":[[0,1,1,"","OpenCLKraskov"],[0,1,1,"","OpenCLKraskovCMI"],[0,1,1,"","OpenCLKraskovMI"]],"idtxl.estimators_opencl.OpenCLKraskov":[[0,2,1,"","is_analytic_null_estimator"],[0,2,1,"","is_parallel"]],"idtxl.estimators_opencl.OpenCLKraskovCMI":[[0,2,1,"","estimate"]],"idtxl.estimators_opencl.OpenCLKraskovMI":[[0,2,1,"","estimate"]],"idtxl.estimators_pid":[[0,1,1,"","SydneyPID"],[0,1,1,"","TartuPID"]],"idtxl.estimators_pid.SydneyPID":[[0,2,1,"","estimate"],[0,2,1,"","is_analytic_null_estimator"],[0,2,1,"","is_parallel"]],"idtxl.estimators_pid.TartuPID":[[0,2,1,"","estimate"],[0,2,1,"","is_analytic_null_estimator"],[0,2,1,"","is_parallel"]],"idtxl.estimators_python":[[0,1,1,"","PythonKraskovCMI"]],"idtxl.estimators_python.PythonKraskovCMI":[[0,2,1,"","estimate"],[0,2,1,"","is_analytic_null_estimator"],[0,2,1,"","is_parallel"]],"idtxl.idtxl_exceptions":[[0,5,1,"","AlgorithmExhaustedError"],[0,5,1,"","BROJA_2PID_Exception"],[0,5,1,"","JidtOutOfMemoryError"],[0,4,1,"","package_missing"]],"idtxl.idtxl_io":[[0,4,1,"","export_brain_net_viewer"],[0,4,1,"","export_networkx_graph"],[0,4,1,"","export_networkx_source_graph"],[0,4,1,"","import_fieldtrip"],[0,4,1,"","import_matarray"],[0,4,1,"","load_json"],[0,4,1,"","load_pickle"],[0,4,1,"","save_json"],[0,4,1,"","save_pickle"]],"idtxl.idtxl_utils":[[0,4,1,"","argsort_descending"],[0,4,1,"","autocorrelation"],[0,4,1,"","calculate_mi"],[0,4,1,"","combine_discrete_dimensions"],[0,4,1,"","conflicting_entries"],[0,4,1,"","discretise"],[0,4,1,"","discretise_max_ent"],[0,4,1,"","equal_dicts"],[0,4,1,"","print_dict"],[0,4,1,"","remove_column"],[0,4,1,"","remove_row"],[0,4,1,"","separate_arrays"],[0,4,1,"","sort_descending"],[0,4,1,"","standardise"],[0,4,1,"","swap_chars"],[0,1,1,"","timeout"]],"idtxl.idtxl_utils.timeout":[[0,2,1,"","timeout_handler"]],"idtxl.multivariate_mi":[[0,1,1,"","MultivariateMI"]],"idtxl.multivariate_mi.MultivariateMI":[[0,2,1,"","analyse_network"],[0,2,1,"","analyse_single_target"]],"idtxl.multivariate_pid":[[0,1,1,"","MultivariatePID"]],"idtxl.multivariate_pid.MultivariatePID":[[0,2,1,"","analyse_network"],[0,2,1,"","analyse_single_target"]],"idtxl.multivariate_te":[[0,1,1,"","MultivariateTE"]],"idtxl.multivariate_te.MultivariateTE":[[0,2,1,"","analyse_network"],[0,2,1,"","analyse_single_target"],[0,2,1,"","getit"]],"idtxl.network_analysis":[[0,1,1,"","NetworkAnalysis"]],"idtxl.network_analysis.NetworkAnalysis":[[0,3,1,"","current_value"],[0,2,1,"","resume_checkpoint"],[0,3,1,"","selected_vars_full"],[0,3,1,"","selected_vars_sources"],[0,3,1,"","selected_vars_target"]],"idtxl.network_comparison":[[0,1,1,"","NetworkComparison"]],"idtxl.network_comparison.NetworkComparison":[[0,2,1,"","calculate_link_te"],[0,2,1,"","compare_between"],[0,2,1,"","compare_links_within"],[0,2,1,"","compare_within"]],"idtxl.network_inference":[[0,1,1,"","NetworkInference"],[0,1,1,"","NetworkInferenceBivariate"],[0,1,1,"","NetworkInferenceMI"],[0,1,1,"","NetworkInferenceMultivariate"],[0,1,1,"","NetworkInferenceTE"]],"idtxl.results":[[0,1,1,"","AdjacencyMatrix"],[0,1,1,"","DotDict"],[0,1,1,"","Results"],[0,1,1,"","ResultsMultivariatePID"],[0,1,1,"","ResultsNetworkAnalysis"],[0,1,1,"","ResultsNetworkComparison"],[0,1,1,"","ResultsNetworkInference"],[0,1,1,"","ResultsPID"],[0,1,1,"","ResultsSingleProcessAnalysis"],[0,1,1,"","ResultsSingleProcessRudelt"]],"idtxl.results.AdjacencyMatrix":[[0,2,1,"","add_edge"],[0,2,1,"","add_edge_list"],[0,2,1,"","get_edge_list"],[0,2,1,"","n_edges"],[0,2,1,"","n_nodes"],[0,2,1,"","print_matrix"]],"idtxl.results.Results":[[0,2,1,"","combine_results"]],"idtxl.results.ResultsMultivariatePID":[[0,2,1,"","get_single_target"]],"idtxl.results.ResultsNetworkAnalysis":[[0,2,1,"","get_single_target"],[0,2,1,"","get_target_sources"],[0,3,1,"","targets_analysed"]],"idtxl.results.ResultsNetworkComparison":[[0,2,1,"","get_adjacency_matrix"],[0,2,1,"","get_single_target"],[0,2,1,"","get_target_sources"],[0,2,1,"","print_edge_list"]],"idtxl.results.ResultsNetworkInference":[[0,2,1,"","get_adjacency_matrix"],[0,2,1,"","get_source_variables"],[0,2,1,"","get_target_delays"],[0,2,1,"","print_edge_list"]],"idtxl.results.ResultsPID":[[0,2,1,"","get_single_target"]],"idtxl.results.ResultsSingleProcessAnalysis":[[0,2,1,"","get_significant_processes"],[0,2,1,"","get_single_process"],[0,3,1,"","processes_analysed"]],"idtxl.results.ResultsSingleProcessRudelt":[[0,2,1,"","get_single_process"],[0,3,1,"","processes_analysed"]],"idtxl.single_process_analysis":[[0,1,1,"","SingleProcessAnalysis"]],"idtxl.stats":[[0,4,1,"","ais_fdr"],[0,4,1,"","check_n_perm"],[0,4,1,"","max_statistic"],[0,4,1,"","max_statistic_sequential"],[0,4,1,"","max_statistic_sequential_bivariate"],[0,4,1,"","mi_against_surrogates"],[0,4,1,"","min_statistic"],[0,4,1,"","network_fdr"],[0,4,1,"","omnibus_test"],[0,4,1,"","syn_shd_against_surrogates"],[0,4,1,"","unq_against_surrogates"]],"idtxl.visualise_graph":[[0,4,1,"","plot_mute_graph"],[0,4,1,"","plot_network"],[0,4,1,"","plot_network_comparison"],[0,4,1,"","plot_selected_vars"]],idtxl:[[0,0,0,"-","active_information_storage"],[0,0,0,"-","bivariate_mi"],[0,0,0,"-","bivariate_pid"],[0,0,0,"-","bivariate_te"],[0,0,0,"-","data"],[0,0,0,"-","embedding_optimization_ais_Rudelt"],[0,0,0,"-","estimators_Rudelt"],[0,0,0,"-","estimators_jidt"],[0,0,0,"-","estimators_mpi"],[0,0,0,"-","estimators_multivariate_pid"],[0,0,0,"-","estimators_opencl"],[0,0,0,"-","estimators_pid"],[0,0,0,"-","estimators_python"],[0,0,0,"-","idtxl_exceptions"],[0,0,0,"-","idtxl_io"],[0,0,0,"-","idtxl_utils"],[0,0,0,"-","multivariate_mi"],[0,0,0,"-","multivariate_pid"],[0,0,0,"-","multivariate_te"],[0,0,0,"-","network_analysis"],[0,0,0,"-","network_comparison"],[0,0,0,"-","network_inference"],[0,0,0,"-","results"],[0,0,0,"-","single_process_analysis"],[0,0,0,"-","stats"],[0,0,0,"-","visualise_graph"]]},objnames:{"0":["py","module","Python module"],"1":["py","class","Python class"],"2":["py","method","Python method"],"3":["py","property","Python property"],"4":["py","function","Python function"],"5":["py","exception","Python exception"]},objtypes:{"0":"py:module","1":"py:class","2":"py:method","3":"py:property","4":"py:function","5":"py:exception"},terms:{"0":[0,1,2,3,4,5,6,7,8],"00001":[0,7],"005":[0,7],"00561":[0,7],"00629":[0,7],"0068910":0,"00706":[0,7],"00792":[0,7],"00889":[0,7],"00998":[0,7],"01":[0,7],"010":[0,5],"0109462":[0,1],"01119":[0,7],"01256":[0,7],"01409":[0,7],"01581":[0,7],"016":[0,7],"01774":[0,7],"01991":[0,7],"02233":[0,7],"025":[0,7],"02506":[0,7],"0262":[0,5],"02812":[0,7],"03":6,"03155":[0,7],"03356":[0,7],"0354":[0,7],"03972":[0,7],"04":[0,7],"04456":[0,7],"05":[0,3,4,5,7],"051112":[0,5,7],"0561":[0,7],"06295":[0,7],"066138":[0,2],"07063":[0,7],"07924":[0,7],"08891":[0,7],"09976":[0,7],"0s":[0,7],"1":[0,1,2,3,5,6,7,8],"10":[0,1,2,5,6,7],"100":[0,5,7],"1000":[0,1,7],"10000":[0,1,6],"1004":[0,7],"1007":[0,1,5],"1016":[0,7],"1024":[0,2],"11":[0,2,6],"1101":6,"1103":[0,5,7],"11194":[0,7],"11936":[0,7],"12559":[0,7],"1371":[0,1],"14":[0,1,7],"14092":[0,7],"15":[0,3,5,7],"15479":[0,7],"15811":[0,7],"16":[0,2,7],"17":[0,7],"17741":[0,7],"19":[0,2],"19905":[0,7],"1995":[0,8],"1d":[0,2,7],"1e":[0,2],"1s":[0,6,7],"1st":[0,1],"1th":[0,1],"2":[0,1,2,3,5,6,7,8],"20":[0,2,7],"200":[0,2,3,5,7],"2000":[0,2,5],"2001":[0,1],"2002":[0,3,7],"2004":[0,2],"2010":[0,7],"2011":[0,5,7],"2012":[0,2,5,7],"2013":0,"2014":[0,1,2,7],"2015":6,"2017":[0,2],"2018":[0,2],"2020":[0,7],"2021":[0,6,7],"208":[0,2,7],"2161":[0,2,7],"2183":[0,2,7],"21th":6,"22334":[0,7],"23342":[0,7],"25":[0,3,7],"250":[0,7],"25059":[0,7],"2515":[0,7],"25594":[0,7],"27":[0,7],"271":[0,2],"28117":[0,7],"2d":[0,2,3],"2pid":[0,2],"3":[0,1,2,5,6,7,8],"30":[0,5],"3000":[0,1],"31548":[0,7],"3389":[0,7],"3390":[0,2,7],"35397":[0,7],"37":6,"39":[0,2,7],"39716":[0,7],"4":[0,1,2,3,5,7,8],"40919":[0,7],"44563":[0,7],"45":[0,5,6],"45625":[0,7],"461":[0,2,5],"463":[0,1],"464":[0,5],"467050v1":6,"474":[0,1],"5":[0,1,5,7,8],"500":[0,3,4,5,7],"5000":[0,1],"50594":[0,7],"530":[0,2],"53973":[0,7],"54":[0,2,7],"56101":[0,7],"58114":[0,7],"6":[0,1,2,3,7],"60":[0,7],"62946":[0,7],"63":[0,7],"67":[0,5],"69":[0,2],"7":[0,1,7,8],"70627":[0,7],"725":6,"734":6,"77407":[0,7],"79245":[0,7],"8":[0,1,2,7],"81171":[0,7],"83":[0,5,7],"84":[0,1],"85":[0,2,5],"870":[0,3],"878":[0,3],"88914":[0,7],"9":[0,1],"97":[0,7],"97164":[0,7],"99054":[0,7],"99763":[0,7],"\u03ba":[0,7,8],"\u03c41":[0,7,8],"abstract":[0,2,7],"boolean":[0,2],"break":[0,2],"case":[0,6,7],"class":[0,2,3,4,5,6,7,9],"default":[0,1,2,3,4,5,6,7,8],"do":0,"export":0,"final":[0,2,5,7],"float":[0,1,2,3,4,5,6,7,8],"function":[0,1,2,5,6,7,9],"g\u00f6ttingen":[0,7],"import":[0,7],"int":[0,1,2,3,4,5,6,7,8],"long":[0,7],"new":[0,1,2,7],"null":[0,2],"return":[0,1,2,3,4,5,6,7,8],"true":[0,1,2,3,4,5,6,7,8],"v\u00f6gler":[0,7],"var":[0,1,2],"while":[0,1,2,7],A:[0,1,2,3,4,5,6,7,8],As:6,At:6,For:[0,1,2,3,5,6,7],If:[0,1,2,3,4,6,7,9],In:[0,6,7],It:[0,6],ONE:[0,1],One:[0,6,7],The:[0,2,3,4,5,6,7,9],There:[0,8],These:[0,7],To:[0,2,3,5,7,8],__main__:[0,2],__name__:[0,2],_create_surrogate_t:[0,3],_process0:[0,7],_single_target:[0,8],ab:[0,7,8],about:[0,4,7,8],abov:[0,2,7],absenc:[0,7],absolut:[0,8],abzing:[0,2],accept:[0,1],access:[0,8],accord:[0,1,3,6],achiev:6,acm:6,across:[0,3,6,7],actic:[0,2],activ:[0,2,3,8,9],active_information_storag:[7,9,10],activeinformationstorag:[0,7],actual:[0,1,2,3,6],ad:[0,2,5,7,8],adapt:[0,3],add:[0,2,5,6,7],add_condit:[0,5,7],add_edg:0,add_edge_list:0,addit:[0,1,7],addition:[0,7,8],adjac:[0,6,8],adjacency_matrix:0,adjacencymatrix:[0,8],advanc:[0,7],after:[0,3],again:[0,2,5,6],against:[0,3],ai:[0,2,3,8,9],ais_fdr:[0,3,7],ais_pval:[0,8],ais_sign:[0,8],ais_tot:[0,7,8],aka:[0,7],al:6,albert:[0,2],alg_num:[0,2],algorithm:[0,2,6,8,9],algorithm_num:[0,2],algorithmexhaustederror:[0,3],all:[0,1,2,3,4,5,6,7,8],allow:[0,1,2,3],alon:[0,2],along:[0,3],alph1:[0,2],alph2:[0,2],alph:[0,2,7],alph_s1:[0,2,7],alph_s2:[0,2,7],alph_t:[0,2,7],alpha:[0,3,4,5,6,7,8],alpha_:[0,5,7],alpha_comp:[0,4],alpha_fdr:[0,3],alpha_max_seq:[0,3],alpha_max_stat:[0,3],alpha_mi:[0,3,8],alpha_min_stat:[0,3],alpha_ml:[0,7],alpha_omnibu:[0,3],alphabet:[0,2],alphabet_s:[0,7],alphabet_size_past:[0,7],alphc:[0,2],alreadi:[0,2,5],also:[0,6],alwai:[0,2],among:[0,6,7],an:[0,1,2,3,4,7,8],analys:[0,1,8],analyse_auto_mi:[0,7,8],analyse_network:[0,5,7],analyse_single_process:[0,7],analyse_single_target:[0,5,7],analysi:[0,1,2,3,4,5,8,9],analysis_setup:[0,3],analyt:[0,2,7],analyticnulldistribut:[0,2],analyz:[0,7],ani:[0,1,6],anoth:[0,2],append:6,appli:[0,2,3,7],approach:0,ar:[0,1,2,3,4,5,6,7,8],arang:[0,1],arbitrari:[0,2],arg:[0,1,2,3,4,5,6,7,8],argsort_descend:[0,3],argument:[0,2,6],around:[0,7],arrai:[0,1,2,3,4,5,6,7,8],array_nam:0,arxiv:[0,7],assign:[0,4],associ:6,assum:[0,2],assumpt:[0,3,7],astyp:[0,7],attain:[0,7],attemp:[0,2],attempt:[0,2],attribut:[0,1,3,5,6,7,8],auto:[0,1,7],auto_mi:[0,7,8],auto_mi_bin_s:[0,7,8],auto_mi_bin_size_set:[0,7],auto_mi_delai:[0,7,8],auto_mi_max_delai:[0,7,8],autocorrel:[0,3,7,8],autodetect:0,automat:[0,7],autoregress:0,avail:[0,2,7,8],averag:[0,2,7],avg:[0,8],avoid:[0,2],ax:[0,1],axi:[0,3],ay:[0,2,7],b:[0,4,6,8],baccala:[0,1],back:[0,1,7],base:[0,2,3,4,5,7],basi:0,basic:[0,3],bayesian:[0,7],bayesian_bias_criterion:[0,7],bbc:[0,7],bbc_term:[0,7],bbc_toler:[0,7],becaus:[0,1,2,3,6,7],becker:[0,7],been:[0,2,4,7,8],beer:[0,7],befor:[0,2,3,8],being:9,below:[0,7],benjamini:[0,8],bertsching:[0,2,7],best:6,beta:[0,7],beta_map:[0,7],between:[0,2,3,4,5,6,7],bia:[0,7],bialek:[0,7],bias:[0,7],big:[0,3],bigger:[0,3],bin:[0,2,3,7,8],bin_siz:[0,7],binari:[0,7,8],biol:[0,1],biologi:[0,7],biorxiv:6,bit:[0,2],bivar:0,bivari:[0,2,3,8,9],bivariate_mi:[5,9,10],bivariate_pid:[7,9,10],bivariate_t:[5,9,10],bivariatemi:[0,5],bivariatepid:[0,7],bivariatet:[0,5],block:[0,1,7],block_siz:[0,1],bmi:9,bnv:0,bool:[0,1,2,3,4,5,6,7,8],bootstrap:[0,7],bootstrap_ci_percentile_hi:[0,7],bootstrap_ci_percentile_lo:[0,7],bootstrap_ci_use_sd:[0,7],borgwardt:6,both:[0,2,3,4,7,8],bound:[0,2,7],boundari:[0,2],brain:0,brainnet:0,broja:[0,2],broja_2pid:[0,2],broja_2pid_except:0,bte:9,build:6,c:[0,3],calcclass:[0,2],calcul:[0,1,2,3,4,5,6,7],calculate_link_t:[0,4],calculate_mi:[0,3],call:[0,2,3,5,6,7,8],cambridg:[0,7],can:[0,1,2,3,4,5,6,7,8],candid:[0,3,5,7],candidate_set:[0,3],cannot:[0,2,3],carri:6,caus:0,causal:[0,5,7],cf:[0,7],chang:[0,2],channel:0,charact:[0,1,3],characterist:[0,7,8],check:[0,2,3,4,6,7],check_input:[0,7],check_n_perm:[0,3],checkpoint:[0,5,7],child:[0,2,7],choic:6,choos:[0,3],chosen:[0,1,7],chunk:[0,2,7],ci:[0,7],circular:[0,1],ckp:0,close:[0,7],cmi:[0,2,4,5,7],cmi_diff_ab:[0,8],cmi_estim:[0,4,5,7,8],code:[0,2,6],coding_list:6,coeffici:[0,1,3],coefficient_matric:[0,1],coher:[0,1],collect:[0,4],color:0,column:[0,3],com:[0,2,7],combin:[0,1,3,6,7],combine_discrete_dimens:[0,3],combine_result:0,come:0,common:[0,2],common_estimate_surrogates_analyt:[0,2],commonli:0,comp:[0,5],compar:[0,1,3,4,8],compare_between:[0,4],compare_links_within:[0,4],compare_within:[0,4],comparison:[0,3,6,9],complet:[0,3,7],complex:[0,2,6,7],comprehens:0,comput:[0,2,5,6,7],compute_ci:[0,7],computecombinedvalu:[0,3],concaten:[0,2],concentr:[0,7],concept:[0,1],condit:[0,2,3,4,5,6,7,8],conditionalmutualinfocalculatormultivariategaussian:[0,2],cone:[0,2],cone_solv:[0,2],confer:6,confid:[0,2,7],conflict:0,conflicting_entri:[0,3],connect:[0,4,5,8],connectom:0,consequ:[0,7],consid:[0,3,5,6],consider:[0,7],consist:[0,1,5,6],consol:[0,2,3,4,5,7,8],constant:[0,3],construct:[0,5],contain:[0,2,3,5,7,8],content:[6,10],context:[0,2,3],continu:[0,2,3],contribut:[0,7],control:[0,2,4],conveni:[0,8],converg:[0,2],convert:[0,6,7],coordin:0,copi:[0,3],corr:[0,3],correct:[0,3,5,6,7,8],correct_by_target:[0,3],correl:[0,3],correspond:[0,1,2,6,8],could:0,count:[0,1,2,6,7],count_discord:6,count_discordants_wylight:6,count_subgraph:6,count_subgraph_wylight:6,coupl:[0,1],cpu:[0,9],crash:[0,5,7],creat:[0,1,3,4,6,7],creation:[0,1,3,4,5,7],criterion:[0,7,8],critic:[0,3,4,5,7],current:[0,1,2,3,5,6,7,8],current_min_freq:6,current_min_p:6,current_symbol_arrai:[0,7],current_valu:[0,1,5,7,8],cybern:[0,1],d2_log_rho:[0,7],d2_log_rho_xi:[0,7],d2_xi:[0,7],d3_xi:[0,7],d:[0,1,2,3,7,8],d_log_rho:[0,7],d_log_rho_xi:[0,7],d_xi:[0,7],data:[2,3,4,5,6,8,9,10],data_1:[0,1],data_2:[0,1],data_a:[0,4],data_b:[0,4],data_format:6,data_mut:[0,1],data_new:[0,1],data_properti:[0,8],data_set_a:[0,4],data_set_b:[0,4],data_spiketim:[0,7],de:[0,5],debug:[0,2,7],decod:6,decode_adjac:6,decomposit:[0,2,8,9],decreas:[0,2],decrement:[0,2],defin:[0,2,3],degre:[0,2,3],delai:[0,2,7,8],delet:[0,7],delimit:[0,7],delta:[0,7],denomin:[0,3],depend:[0,1,4,6,8,9],dependent_var:[0,7],depth:[0,5,7,8],deriv:[0,3,7],descend:[0,3],describ:[0,2,5,6,7,8],descric:[0,2],descript:[0,5,7],design:6,desir:6,detail:[0,2,3,4,5,7,8],detect:[0,3,4,5,7],determin:[0,1,6,7,8],determine_tarone_factor:6,deviat:[0,1,3,7],devic:[0,2],df:[0,3],dict:[0,1,2,3,4,5,6,7,8],dict_1:[0,3],dict_2:[0,3],dictionari:[0,2,3,7,8],dietterich:[0,7],diff_ab:[0,8],differ:[0,1,2,3,4,6,7,8],differenti:[0,2,7],digraph:0,dim_ord:[0,1],dimens:[0,1,2,3],dimension:[0,1,3],direct:[0,1,2,6],dirichlet:[0,7],discard:[0,7],discord:6,discoveri:[0,3,6],discret:[0,1,2,3],discretis:[0,2,3],discretise_max_:[0,3],discretise_method:[0,2],discretisemaxentropi:[0,3],discuss:9,disk:[0,5,7],displai:0,display_edge_label:0,distanc:[0,2],distribut:[0,2,3,6,7,8],divid:[0,3],docstr:[0,5,7,8],document:[0,2,3,4,5,7,8],doe:[0,2,3,6],doi:[0,1,2,5,7],don:0,done:[0,3,7],dot:[0,8],dotdict:[0,7],doubl:[0,2],down:[0,3],drawn:[0,7],due:[0,2,7],duplic:0,dure:[0,7],dynam:[0,1,2,9],e109462:[0,1],e16042161:[0,2,7],e68910:0,e:[0,1,2,3,4,5,6,7,8],each:[0,1,2,3,4,5,6,7,8],easi:[0,7],east:6,eco:[0,2],edg:[0,8],editor:[0,7],effect:[0,4,5,7,8],effici:[0,6,7],either:[0,2,3,5,6,7,8],els:[0,7],emb:[0,7],embed:[0,1,2,4,5,8,9],embedding_number_of_bins_set:[0,7],embedding_optimization_ais_rudelt:[7,9,10],embedding_past_range_set:[0,7],embedding_scaling_exponent_set:[0,7],embedding_step_s:[0,7],empir:[0,2],empti:[0,1,6],enabl:[0,5,7],encod:6,encode_adjac:6,engin:0,enough:[0,3,6],enter:[0,8],entri:[0,1,3,4,6,7,8],entropi:[0,1,2,3,7,8,9],enumerate_frequent_graph:6,enumerate_frequent_subgraph:6,enumerate_significant_subgraph:6,ep:[0,7],eq:[0,1],equal:[0,2,3],equal_dict:[0,3],equiv:[0,7],equival:[0,7,8],err:0,error:[0,2],essenti:[0,7],est:[0,2],establish:[0,1],estim:[0,1,3,4,5,6,8,9],estimate_parallel:[0,2],estimate_surrogates_analyt:[0,2,7],estimation_method:[0,7,8],estimators_:[0,4,5,7],estimators_jidt:[2,9,10],estimators_mpi:[2,9,10],estimators_multivariate_pid:[9,10],estimators_opencl:[2,9,10],estimators_pid:[2,7,9,10],estimators_python:[2,9,10],estimators_rudelt:[7,9,10],et:6,evalu:6,even:6,evenli:[0,3],event:[0,7],everi:[0,2,5,7],ex:[0,2,3],exact:[0,2],exampl:[0,1,3,4,5,7],except:[0,3],exception_messag:[0,3],exclud:[0,5],exist:[0,1],expect:[0,1,7],experiment:[0,4],explan:0,expon:[0,7,8],exponenti:[0,2],export_brain_net_view:0,export_networkx_graph:0,export_networkx_source_graph:0,extend:6,extend_mcnemar:6,extend_wi:6,extend_wy_light:6,extend_wy_light_mcnemar:6,extend_wy_mcnemar:6,extens:[0,5,6,7],extract:[0,7],f:[0,1,6,7],face:[0,4],factor:[0,6,7],fae:[0,1,5,7],fall:[0,1],fals:[0,1,2,3,5,7],far:6,fast:[0,2,6],faster:[0,3],fdr:[0,3,5,8],fdr_constant:[0,3],fdr_correct:[0,5,7],femal:[0,4],fewer:[0,8],field:[0,3],fieldtrip:0,fieldtriptoolbox:0,fifth:0,figur:0,file:[0,5,7],file_nam:0,file_path:0,file_vers:0,filename_ckp:[0,5,7],fill:6,find:[0,2,5,7],fire:[0,7,8],firing_r:[0,7,8],first:[0,1,2,4,6,7,8],first_bin_s:[0,7],fit:[0,1],five:[0,1],fix:[0,1],fl:6,fninf:[0,7],follow:[0,1,7],forc:[0,5,7],form:[0,1,6],format:[0,1,2,7],forth:[0,3],forward:[0,2],found:[0,5,7],four:[0,4,5],fourth:0,free:[0,5],freedom:[0,3],freq:6,frequenc:[0,6,7],frequent:6,frequent_graph:6,frequent_subgraph:6,from:[0,1,2,3,4,5,7,8],front:[0,2,7],fsampl:0,ft_datatype_raw:0,ft_struct_nam:0,full:[0,3,5,6],further:[0,4,5,6,7],futur:[0,2],g:[0,1,2,5,7,8],galusk:[0,7],gaussian:[0,1,2],gc:0,gener:[0,1,3,8],generate_coding_list:6,generate_logistic_maps_data:[0,1],generate_min_p_t:6,generate_mute_data:[0,1,5,7],generate_p_t:6,generate_var_data:[0,1],genoves:[0,3],get:[0,1,7,8],get_adjacency_matrix:[0,8],get_analytic_distribut:[0,2],get_as_list:[0,7],get_auto_mi:[0,7],get_bbc_term:[0,7],get_beta_map:[0,7],get_bootstrap_history_depend:[0,7],get_edge_list:0,get_embed:[0,7],get_embeddings_that_maximise_r:[0,7],get_h0_x_past_cond_x:[0,7],get_h0_x_past_cond_x_eq_x:[0,7],get_h_x_past_cond_x:[0,7],get_h_x_past_uncond:[0,7],get_history_depend:[0,7],get_information_timescale_tau_r:[0,7],get_integration_bound:[0,7],get_marginal_frequencies_of_spikes_in_bin:[0,7],get_max_r_t:[0,7],get_median_number_of_spikes_per_bin:[0,7],get_multipl:[0,7],get_p_x_past_cond_x:[0,7],get_p_x_past_uncond:[0,7],get_p_x_uncond:[0,7],get_past_rang:[0,7],get_r_thresh:[0,7],get_r_tot:0,get_raw_symbol:[0,7],get_realis:[0,1],get_realisations_symbol:[0,7],get_se:[0,1],get_set_of_sc:[0,7],get_shannon_entropi:[0,7],get_shuffled_symbol_count:[0,7],get_significant_process:[0,8],get_single_process:[0,8],get_single_target:[0,8],get_source_vari:[0,8],get_stat:[0,1],get_symbol_count:[0,7],get_target_delai:[0,8],get_target_sourc:[0,8],get_temporal_depth_t_d:[0,7],get_window_delimit:[0,7],getit:0,ghahramani:[0,7],github:[0,2,3,7,9],give:[0,2],given:[0,1,2,3,6,7,8],glass:0,goal:6,good:0,googl:9,gpu:[0,9],gpuid:[0,2],granger:[0,5,7],graph:[0,7],graph_typ:6,grassberg:[0,2],greedi:0,group:[0,6,7,9],groupa:6,groupa_network:6,groupb:6,groupb_network:6,guard:[0,2],guess:[0,7],gutknecht:[0,6,7],h0_sh_x_past_cond_x:[0,7],h0_x_past_cond_x:[0,7],h1:[0,7],h:[0,2,7],h_0:[0,7],h_spike:[0,7,8],h_uncond:[0,7],h_x_past_cond_x:[0,7],h_x_past_uncond:[0,7],ha:[0,1,2,3,4,5,6,7,8],halv:[0,2],handl:0,happen:6,hard:[0,2],have:[0,1,2,3,4,5,7,8,9],hde:[0,7],hdestim:[0,7],hdf5:0,he:0,head:9,hehlotler:[0,3],hellother:[0,3],helper:9,henc:[0,2,5],here:[0,2,7],heurist:[0,7],high:[0,2,8],higher:0,highest:[0,3,8],highest_protocol:0,histori:[0,2,8,9],history_depend:[0,7,8],history_sourc:[0,2],history_target:[0,2],hit:[0,2],hold:[0,8],home:0,hommel:6,hopefulli:0,host:9,hous:[0,4],how:[0,2,7],howev:[0,2],http:[0,1,2,3,5,6,7],human:0,hypothesi:[0,2],i:[0,2,3,4,5,6,7,8],i_1:[0,3],i_2:[0,3],i_corr:[0,7],i_list:0,i_plugin:[0,7],id:[0,2,8],identifi:[0,3,6,8],idtxl:[1,2,3,4,5,6,7,8],idtxl_checkpoint:[0,5,7],idtxl_except:[9,10],idtxl_io:[9,10],idtxl_util:[3,9,10],idx:[0,1,5,7],idx_al:[0,3],idx_list:[0,1],idx_realis:[0,1],idx_singl:[0,3],ie:[0,7],ignor:[0,2,6,7],imag:[0,7],immedi:6,implement:[0,1,2,4,6,7],implicitli:[0,1],import_fieldtrip:0,import_matarrai:0,improv:[0,2],includ:[0,3,6,7,8],incom:[0,2],inconveni:0,increment:[0,2],ind:[0,1],indent:[0,3],independ:[0,2,4,7],index:[0,1,3,4,5,6,7,8,9],indic:[0,1,2,3,5,6,7,8],individu:[0,2,3,5,6,7,8],inf:6,infer:[0,3,4,7,9],infin:6,infinit:[0,2],influenc:[0,5,7],infodynam:[0,3],infor:[0,2],inform:[0,1,3,4,8,9],initi:[0,1,2,6],initial_st:[0,1],initialis:[0,1],inner:[0,2,5],input:[0,2,3,6,7,8],ins:[0,7],insid:0,inspect:[0,7],instanc:[0,1,2,3,4,5,6,7,8],instanti:[0,2],instead:[0,1,2,3,5,7],institut:[0,5],int32:[0,2],intact:[0,1],integ:[0,1,6,7],integr:[0,7],interact:[0,8],intermedi:[0,2],intern:[0,6],interv:[0,7],intial:6,introduc:6,io:[0,3],is_analytic_null_estim:[0,2,7],is_parallel:[0,2,7],issu:9,iter:[0,5,7],ith:6,its:[0,2,7,8],j:[0,2,3,5,7],j_list:0,java:[0,2],jidt:[0,3,9],jidtdiscret:[0,2],jidtdiscreteai:[0,2],jidtdiscretecmi:[0,2],jidtdiscretemi:[0,2],jidtdiscretet:[0,2],jidtestim:[0,2],jidtgaussian:[0,2],jidtgaussianai:[0,2],jidtgaussiancmi:[0,2],jidtgaussianmi:[0,2],jidtgaussiant:[0,2],jidtkraskov:[0,2],jidtkraskovai:[0,2],jidtkraskovcmi:[0,2,5,7],jidtkraskovmi:[0,2],jidtkraskovt:[0,2],jidtoutofmemoryerror:[0,2],joint:[0,2,3,4,5],joseph:[0,2],jost:[0,2,7],journal:[0,1],jpackag:[0,2],jpype:[0,2],json:0,k1:[0,7],k:[0,1,6,7],k_rt:6,kappa:[0,7],kasenburg:6,keep:[0,1,2],kei:[0,3,7,8],keyword:[0,8],km:6,knn:[0,2],knn_finder:[0,2],knowledg:6,kraskov:[0,2],kraskov_k:[0,2],ksg:[0,2],kwarg:[0,2],l:[0,1,5,6,7],label:0,labl:0,lag:[0,1,2,5,6,7,8],lag_mi:[0,2],lags_pid:[0,7],larger:[0,2,6],last:[0,3,7],later:0,latter:[0,2],lattic:[0,8],lazar:[0,3],le:0,least:6,len:[0,3],lenght:[0,7],length:[0,1,2,3,7,8],lett:[0,2,5],level:[0,2,3,4,5,6,7],light:6,like:[0,7,8],likelihood:[0,7],limit:[0,2],lindner:[0,5,7],line:[0,4],linear:[0,1],linearli:[0,7],link:[0,3,4,5,6,8],link_a:[0,4],link_b:[0,4],link_count:6,list:[0,1,3,4,5,6,7,8],lizier:[0,2,3,5,7],llinar:6,load:[0,7],load_json:0,load_pickl:0,local:[0,1,2,7],local_valu:[0,2],locat:[0,6,7],log:[0,7],log_likelihood_dp_alpha:[0,7],logarithm:[0,7],logic:[0,3],logical_xor:[0,7],logist:[0,1],longer:0,look:[0,8,9],lookup:6,loop:[0,2],loos:[0,1],lopez:6,lower:[0,7],lowest:[0,3],m:[0,2,5,6,7],ma:[0,7],machin:[0,2],made:[0,3],mai:[0,2,8],main:[0,2,6,7],make:[0,2,3,7],makkeh:[0,2,7],male:[0,4],manag:[0,3],mani:[0,2],manual:0,map:[0,1,3,6,7],margin:[0,7],marginal_prob:[0,7],marinazzo:[0,1],marx:[0,7],mass:[0,2],mat:0,match:[0,4],matlab:[0,1],matplotlib:0,matric:[0,1,6],matrix:[0,1,6,8],matrixutil:[0,3],max:[0,1,2,3,5],max_depth:6,max_ent:[0,2],max_it:[0,2,7],max_lag:[0,5,7],max_lag_sourc:[0,5],max_lag_target:[0,5],max_p:[0,8],max_p_lag:[0,8],max_seq:[0,5],max_shift:[0,1],max_stat:[0,5,7],max_statist:[0,3],max_statistic_sequenti:[0,3],max_statistic_sequential_bivari:[0,3],max_t:[0,8],max_te_lag:[0,8],max_unsuc_swaps_row_parm:[0,2,7],max_work:[0,2],maxim:[0,7],maximis:[0,5,7],maximum:[0,1,2,3,5,6,7,8],mcnemar:6,mean:6,measur:[0,2,5,6,7,8],median:[0,7],mem:[0,2],memori:[0,6],messag:[0,3],method:[0,1,2,3,4,5,6,7,8],mi:[0,2,3,5,7,8],mi_against_surrog:[0,3],mi_omnibu:[0,5],mi_sign_sourc:[0,5],michael:[0,7],mikhail:[0,2],min:[0,3],min_first_bin_s:[0,7],min_freq:6,min_lag:[0,5],min_lag_sourc:[0,5],min_p_value_t:6,min_stat:[0,5,7],min_statist:[0,3],min_step_for_sc:[0,7],mine:[0,8,9],minimum:[0,3,5,6,7],minimum_p_valu:6,minu:[0,7],misinform:0,miss:0,mit:[0,7],mk:[0,7],mmi:9,mni:0,mni_coord:0,model:[0,1,5,7],modul:[2,4,5,7,8,9,10],moment:[0,7],montalto:[0,1],more:[0,2,5,6,7,9],most:[0,6,7],mpg:[0,5],mpi4pi:[0,2],mpi:[0,9],mpiestim:[0,2],mpiexec:[0,2],mte:9,multi:[0,3],multinomi:[0,7],multipl:[0,1,2,4,6,7,8],multipli:[0,3],multivar:0,multivari:[0,1,2,3,4,8,9],multivariate_mi:[5,9,10],multivariate_pid:[7,9,10],multivariate_t:[5,9,10],multivariatemi:[0,5],multivariatepid:[0,7],multivariatet:[0,3,5,8],must:[0,1,2,3,7],mute:[0,1],mutual:[0,2,3,7,9],n:[0,1,2,3,6,7],n_a:6,n_b:6,n_chunk:[0,2],n_discrete_bin:[0,2],n_edg:0,n_node:[0,8],n_perm:[0,2,3],n_perm_:[0,5,7],n_perm_comp:[0,4],n_perm_max_seq:[0,3,5],n_perm_max_stat:[0,3,5,7],n_perm_mi:[0,3],n_perm_min_stat:[0,3,5,7],n_perm_omnibu:[0,3,5],n_process:[0,1,8],n_realis:[0,1,8],n_realisations_repl:[0,1],n_realisations_sampl:[0,1],n_replic:[0,1],n_sampl:[0,1],name:[0,5,7],nat:[0,2],ndarrai:[0,2],nearest:[0,2],nearli:[0,2],necessari:[0,1,7],necessarili:[0,7],need:[0,2,7],neighborhood:[0,2],neighbour:[0,2],nemenman:[0,7],network:[0,1,3,7,9],network_a:[0,4],network_analysi:[5,7,9,10],network_b:[0,4],network_comparison:[4,9,10],network_fdr:[0,3,5,8],network_infer:[9,10],network_set_a:[0,4],network_set_b:[0,4],networkanalysi:0,networkcomparison:[0,4],networkinfer:0,networkinferencebivari:0,networkinferencemi:0,networkinferencemultivari:0,networkinferencet:0,networkx:0,neural:[0,1,6,9],neuroimag:[0,3],neuroinf:[0,7],neuron:[0,7,8],neurophysiolog:0,neurosci:[0,5],never:[0,2],next:[0,2],nichol:[0,3],nitrc:0,node:[0,3,5,9],node_color:0,node_s:0,nois:[0,1,2],noise_level:[0,2],noise_std:[0,1],nollo:[0,5,7],non:[0,1,3,5,7],none:[0,1,2,3,6,7],nonessenti:[0,7],nonlinear:[0,5,7],nonneg:[0,7],nonuniform:[0,5,7],normal:[0,7],normalis:[0,1,2,7,8],notat:[0,7,8],note:[0,1,2,3,4,5,7,8],novel:[0,1],now:[0,2,3],np:[0,1,2,7],nsb:[0,7],nsb_entropi:[0,7],nsb_estim:[0,7],num_perm:6,num_rep:[0,2,7],num_testable_graph:6,num_thread:[0,2],number:[0,1,2,3,4,5,6,7,8],number_of_bins_d:[0,7],number_of_bootstrap:[0,7],number_of_bootstraps_nonessenti:[0,7],number_of_bootstraps_r_max:[0,7],number_of_bootstraps_r_tot:[0,7],number_of_delai:[0,7],number_of_sc:[0,7],number_of_symbol:[0,7],numbin:[0,3],numer:[0,2],numpi:[0,1,2,3,4,5,6,7,8],o:[0,2],obj:0,object:[0,1,2,3,4,7],observ:[0,2,4,5,6,7],obtain:[0,3,6,7,8],occur:[0,6,7],occurr:[0,6,7],offer:0,often:[0,6,7],olbrich:[0,2,7],old:[0,3],omnibu:[0,3,5],omnibus_pv:0,omnibus_sign:0,omnibus_t:0,omnibus_test:[0,3],onc:[0,2,6],one:[0,2,3,4,6,7,8],onli:[0,1,2,3,5,6,7],opencl:[0,9],openclkraskov:[0,2],openclkraskovcmi:[0,2],openclkraskovmi:[0,2],oppos:[0,2],opt_first_bin_s:[0,7,8],opt_number_of_bins_d:[0,7,8],opt_scaling_k:[0,7,8],optim:[0,2,8,9],optimizationrudelt:[0,7],optimize_single_run:[0,7],option:[0,1,2,3,4,5,7,8],order:[0,1,3],org:[0,1,2,5,6,7],orgin:[0,1],orgini:[],origin:[0,1,2,3,6,7],other:[0,3,5,6,7],otherwis:[0,1,3,7],our:[0,2],out:[0,6],outer:[0,2],outofmemoryexcept:0,output:[0,2,4,5,7,8],output_path:[0,7],output_prefix:[0,7],outsid:[0,2],over:[0,1,2,3,4,5,7],overwrit:[0,1,2],own:[0,5,7],p0_sh_x_past_cond_x:[0,7],p:[0,1,2,3,5,6,7,8],p_0:[0,7],p_valu:[0,3,6],p_value_t:6,p_values_corr:6,p_x_past_cond_x:[0,7],p_x_past_uncond:[0,7],p_x_uncond:[0,7],packag:[2,10],package_miss:0,pad:[0,2],page:[0,3,9],pair:[0,5,6,7],papaxantho:6,paper:[0,1,3],parallel:[0,1,2,3,7],paramet:[0,2,3,4,5,7],parent:[0,2,7,8],part:[0,4,6],partial:[0,1,2,3,8,9],partial_information_decomposit:[0,3],partic:[0,2],particip:[0,4],particular:[0,3,6],partit:[0,3],pass:[0,2,3,4,7],past:[0,2,3,4,5,7,8],past_range_t:[0,7],past_symbol:[0,7],past_symbol_arrai:[0,7],past_symbol_count:[0,7],path:[0,7],patient:[0,4],pattern:[0,1,6],pdf:[0,5],peak:[0,7],per:[0,1,2,6,7],percentil:[0,7],perform:[0,2,3,5,7],performancetip:[0,3],perm:6,perm_rang:[0,1],perm_set:[0,1],perm_typ:[0,1],permut:[0,1,2,3,4,5,6,7],permute_in_tim:[0,3,4,5,7],permute_repl:[0,1],permute_sampl:[0,1,4,5,7],permutet:[],perspect:[0,2],phy:[0,2,5,7],physrev:[0,5,7],physrevlett:[0,5],pickl:0,pid:[0,3,8,9],pid_analysi:[0,7],pid_calc_nam:[0,7],pid_estim:[0,7],pipa:[0,5],pl00007990:[0,1],planck:[0,5],platform:[0,2],plo:[0,1,7],plot:[0,7],plot_mute_graph:0,plot_network:0,plot_network_comparison:0,plot_selected_var:0,plug:[0,2,7],plugin:[0,7],plugin_entropi:[0,7],plugin_estim:[0,7],pmi2:[0,2],point:[0,1,2,3,7,8],pointless:[0,3],pointwis:0,pone:[0,1],porta:[0,5,7],possibl:[0,2,3,6,7],posterior:[0,7],posteriori:[0,7],postprocess:[0,8,9],potenti:[0,5],power:[0,3],practic:[0,2],predict:[0,3],prefix:[0,7],preprint2012_25:[0,5],preprint:[0,5],present:[0,3],preserv:[0,7],press:[0,7],pretti:[0,3],previous:[0,5,7],priesemann:[0,7],principl:[0,7],print:[0,2,3,6,8],print_dict:[0,3],print_edge_list:[0,8],print_matrix:0,printer:[0,3],prior:[0,7],probabl:[0,2,7],problem:9,proc:[0,1],procedur:6,proceed:6,process:[0,1,2,3,4,5,6,7,8],process_set:[0,7],processes_analys:[0,8],produc:[0,7],product:[0,7],program:[0,2],project:0,prokopenko:[0,2,7],properti:[0,1,2,8],propos:[0,1,2],protocol:0,provid:[0,1,2,3,4,7,8],prune:[0,3,5,7],ps:[0,1,7],psr:[0,1],ptw:[0,8],put:[0,3],pval:[0,8],pvalu:[0,5,7,8],pvalue_omnibu:[0,5],pvalues_sign_sourc:[0,5],pypi:[0,2],python3:0,python:[0,3,7,9],pythonkraskovcmi:[0,2],qualit:0,quantifi:[0,2,7],quantil:6,quantiti:[0,2,7],r:[0,1,2,3,5,7,8],r_max:[0,7],r_nsb:[0,7],r_plugin:[0,7],r_tot:[0,7,8],rais:[0,1,2,3],randint:[0,7],random:[0,1,2,7,8],rang:[0,1,2,3,7],rank:[0,2,3],rate:[0,3,7,8],rauh:[0,2,7],raw:[0,1,2,3,4,5,7],raw_symbol:[0,7],re:[0,3,7],reach:6,read:[0,7],realis:[0,1,2,3,5,7,8],realiz:[0,2],rebas:[0,7],recogn:0,recommend:[0,7],record:[0,4,7,8],recording_length:[0,7,8],recurs:[0,2,6],reduc:[0,7],redund:[0,8],refer:[0,1,2,3,5,7],regard:[0,8],regress:[0,1],regular:0,rel:[0,7],relat:[0,7],relationship:[0,2],relev:[0,3,5,6,7],remain:[0,3],reman:[0,3],remanin:[0,3],remov:[0,2,3],remove_column:[0,3],remove_row:[0,3],remove_subresults_single_process:[0,7],repeat:[0,3],repetit:[0,1],repl:[0,1],replic:[0,1,2,3,4,5,7],report:[0,9],repres:[0,1,2,3,6,7,8],represent:[0,7],request:[0,1,3],requir:[0,2,3,6],res_network:[0,8],res_pid:[0,8],resampl:[0,7],reshap:[0,1],respect:[0,2,5],respons:[0,7],restrict:[0,3],result:[1,2,3,4,5,6,7,9,10],resultsa:6,resultsb:6,resultsmultivariatepid:[0,7,8],resultsnetworkanalysi:0,resultsnetworkcomparison:[0,4,8],resultsnetworkinfer:[0,3,5,8],resultspid:[0,7,8],resultssingleprocessanalysi:[0,3,7,8],resultssingleprocessrudelt:[0,7,8],resum:[0,5,7],resume_checkpoint:[0,5,7],retriev:[0,5,7],return_averaged_r:[0,7],return_calc:[0,2],return_count:[0,2],rev:[0,2,5,7],reveal:[0,7],revisit:[0,7],rho:[0,7],rlz:[0,8],rng_seed:[0,2],robot:[0,2],round:[0,3],routin:0,row:[0,2,3],rtot:[0,7,8],rubinov:[0,5],rudelt:[0,7,8],rudeltabstractestim:[0,7],rudeltabstractnsbestim:[0,7],rudeltbbcestim:[0,7],rudeltnsbestimatorsymbolsmi:[0,7],rudeltpluginestimatorsymbolsmi:[0,7],rudeltshufflingestim:[0,7],run:[0,1,2],runtim:[0,2],s10827:[0,5],s1:[0,2,7],s1_unq:[0,8],s2:[0,2,7],s2_unq:[0,8],s3:[0,7],s:[0,1,2,3,4,5,6,7],same:[0,1,2,3,4,5,6,7],sameshima:[0,1],sampl:[0,1,2,3,5,6,7,8],save:[0,2,3,6,7],save_json:0,save_pickl:0,scale:[0,7,8],scaling_k:[0,7],scheme:[0,1],schreiber:[0,2,5],sci:[0,2,7],scipi:[0,3],scipy_kdtre:[0,2],script:[0,2],search:[0,2,5,7,9],second:[0,2,3,4,6,7,8],see:[0,2,3,4,5,7,8],seed:[0,1,2],select:[0,3,4,5,7,8],selected_sources_pv:0,selected_sources_t:0,selected_var:[0,8],selected_vars_ful:[0,5,7],selected_vars_sourc:[0,5,8],selected_vars_target:[0,5,8],self:[0,6,7],separ:[0,3,5,7],separate_arrai:[0,3],sequenc:[0,2],sequenti:[0,3],seri:[0,1,2],set:[0,1,2,3,4,5,6,7,8],set_data:[0,1],setup:[0,3],sever:[0,1],sh:[0,7],shafe:[0,7],shape:[0,3],share:[0,2,3,7,8],shd_s1_s2:[0,8],shift:[0,1],shorter:[0,2],should:[0,2,3,7],show:[0,7],shuffl:[0,1,3,4,5,7],shuffling_mi:[0,7],siam:6,siamintern:6,side:[0,4],sigkdd:6,sign:[0,7],sign_ominbu:[0,5],sign_sourc:0,signal:0,signific:[0,3,4,5,7,8,9],significant_graph:6,significantli:[0,3],significantsubgraphmin:6,similar:[0,2,7,8],simple_depth_first:6,simple_depth_fist:6,simplic:[0,7],simplifi:[0,7],simul:[0,1,7],singl:[0,3,4,5,7,8],single_process_analysi:[9,10],singleprocessanalysi:0,singleton:0,size:[0,1,2,6,7,8],sklearn_balltre:[0,2],sklearn_kdtre:[0,2],slice:[0,1],slice_permute_repl:[0,1],slice_permute_sampl:[0,1],slid:[0,7],slurm:[0,2],slurm_ntask:[0,2],small:[0,1,3],smaller:[0,2,3,6],smallest:[0,6,7,8],sn:0,so:[0,2,3,6,7],soft:[0,2],softwar:0,solver:[0,2],solver_arg:[0,2],some:[0,2,7],soon:6,sort:[0,3],sort_descend:[0,3],sortind:[0,3],sourc:[0,1,2,3,4,5,6,7,8],source_1:[0,8],source_2:[0,8],source_i:[0,8],source_set:[0,5],source_target_delai:[0,2],sources_test:0,space:[0,2,5,7],spawn:[0,2],specif:[0,8],specifi:[0,1,5,6,7,8],spike:[0,8,9],spike_tim:[0,7],spikes_in_window:[0,7],split:[0,2],spr:[0,1],squar:[0,1],squeez:0,srun:[0,2],stage:[0,2],stai:[0,1],stamp:0,stand:0,standard:[0,1,3,7],standardis:[0,1,2,3,8],start:[0,1],stat:[5,7,8,9,10],state:[0,1,2,5,7,8],statis:[0,3],statist:[0,1,3,4,5,7,8],statistic_omnibu:[0,5],statistic_sign_sourc:[0,5],stats_typ:[0,4],step:[0,5,6,7],stochast:[0,1,8],stoegbauer:[0,2],stop:[0,3,6],storag:[0,2,3,8,9],store:[0,1,8],str:[0,1,2,4,5,7,8],strategi:[0,1],string:[0,1,3,5,6,7,8],structur:[0,1],studi:[0,2],style:0,subgraph:[0,8,9],subject:[0,4,6],submodul:[9,10],subplot:0,subset:[0,3,7],success:6,suffici:[0,1,7],sugiyama:6,sum:[0,7],sum_i:[0,3],summar:[0,7],summari:6,supergraph:6,support:[0,2,7],sure:[0,2],surrog:[0,1,2,3,4,5,7,8],surrogate_distribut:[0,8],swap:[0,1,2,3],swap_char:[0,3],sxpid:[0,7],sydneypid:[0,2,7],symbol:[0,7],symbol_arrai:[0,7],symbol_array_to_binari:[0,7],symbol_binari:[0,7],symbol_binary_to_arrai:[0,7],symbol_block_length:[0,7],symbol_count:[0,7],symmetr:[0,7],syn_s1_s2:[0,8],syn_shd_against_surrog:[0,3],synergist:[0,2,3,7,8],system:[0,2,7],t:[0,2,3,5,7,8],t_0:[0,7],t_d:[0,7,8],t_max:[0,7],tabl:[0,3,6],tail:[0,4],tail_comp:[0,4],take:[0,1,7],taken:[0,2,3],target1:[0,7],target:[0,2,3,4,5,6,7,8],target_r:[0,7],targets_analys:[0,8],taron:6,tartupid:[0,2],task:[0,2,4,7],tau:[0,2,7],tau_1:[0,7],tau_r:[0,7,8],tau_sourc:[0,2,5],tau_target:[0,2,5],td:[0,7,8],te:[0,2,3,4,5,7,8],te_max_candid:[0,3],te_min_candid:[0,3],technic:[0,2],techniqu:[0,5,7],tempor:[0,1,2,5,7,8],term:[0,7],test:[0,1,2,3,4,5,6,7,8],testabl:6,text:0,th:6,than:[0,2,3,6,7],thei:[0,2,6,7],theiler:[0,2],theiler_t:[0,2],them:0,theoret:[0,3,8,9],theori:0,therefor:[0,7],thereof:[0,7],thi:[0,1,2,3,4,5,6,7,8],third:[0,2,7],those:0,thread:[0,2,3],three:[0,1,2,5,7],threshold:[0,3],through:[0,5,7],thu:[0,1,2,7],time:[0,1,2,3,4,5,6,7,8],timeout:[0,3],timeout_dur:[0,3],timeout_handl:0,timescal:[0,7,8],timescale_minimum_past_rang:[0,7],tmax:[0,7],to_be_extend:6,todo:[0,7],togeth:[0,7],toggl:[0,4,5,7],toler:[0,7],too:[0,1,2],tool:[0,7],toolbox:[0,1],toolkit:[0,2],tot:[0,7,8],total:[0,1,2,6,7,8],toward:[0,7],tracker:9,train:[0,7,8],tranfer:[0,8],transfer:[0,1,2,3,4,8,9],treat:[0,5],trial:[0,1,2],trigger:[0,3],triplet:6,tupl:[0,1,3,5,6,7,8],tutori:9,two:[0,1,2,3,4,5,6,7,8],type:[0,1,2,4,7],typeerror:[0,1],typic:[0,7],u:[0,1],unbias:[0,7],uncorrect:[0,6,8],uncorrel:[0,1],under:[0,2,4,6,7],understand:[0,7],undirect:6,unequ:[0,3],uniform:[0,2,5,7],union:[0,6,8],union_indic:6,uniqu:[0,2,3,7,8],unit:[0,4],univari:[0,3],unnorm:[0,7],unnormalized_posterior:[0,7],unq_against_surrog:[0,3],unsuccess:[0,2],until:[0,2],unweight:[0,8],up:[0,4,6,7],updat:6,upon:0,upper:[0,2,7],us:[0,1,2,3,4,5,6,7,8],usag:[0,2],use_al:[0,2],user:[0,9],usiz:[0,2],util:[0,9],utilis:[0,2],utl:[0,7],v4:0,v6:0,v7:0,v:[0,7],valid:0,valu:[0,1,2,3,4,5,6,7,8],var1:[0,2],var2:[0,2],vari:[0,2],variabl:[0,1,2,3,4,5,7,8],variou:[0,1],vars_count:[0,8],vector:[0,1,3],verbos:[0,2,4,5,6,7],version:[0,2,6],via:[0,1,2,5,6,7,8],vicent:[0,2,5],viewer:0,virtualis:[0,2],visual:[0,7],visualis:0,visualise_graph:[9,10],visul:[0,7],vitrualis:[0,2],vs:[0,4],vstack:[0,7],w:[0,7],wa:[0,1,2,4,7,8],wai:[0,8],wait:[0,3],wang:0,want:[0,9],warn:0,we:[0,1,7],weight:[0,8],weight_typ:0,well:[0,2,3,7],were:[0,5,7,8],westfal:6,westfall_young:6,westfall_young_light:6,what:[0,7],when:[0,1,2,3,5,7],where:[0,1,2,3,5,6,7,8],whether:[0,7,8],which:[0,1,2,3,4,6,7],whole:[0,3,4,5,7],whose:[0,7],wibral:[0,5,6,7],wiki:[0,3,9],william:[0,7],window:[0,2,7],wise:[0,1],within:[0,1,4,6],without:[0,5,7],workaround:[0,2],worker:[0,2],wrapper:[0,2],write:[0,5,7],write_ckp:[0,5,7],written:0,wrt:[0,5,7,8],www:[0,5,6],wy:6,wy_algorithm:6,wy_level_light:6,wy_light:6,x:[0,1,2,3,7],x_past:[0,7],xi:[0,7],xia:0,xl:0,y:[0,2,7],yield:[0,7,8],you:[0,9],young:6,your:[0,2],z:[0,1,2,3,7,8],zero:[0,7],zomaya:[0,2,7]},titles:["idtxl package","The Data Class","Information theoretic estimators","Helper functions","Network comparison","Algorithms for network inference","Postprocessing of inferred networks","Algorithms for the analysis of node dynamics","The Results Class","Welcome to IDTxl\u2019s documentation!","idtxl"],titleterms:{"class":[1,8],"function":3,The:[1,8],activ:7,active_information_storag:0,ai:7,algorithm:[5,7],analysi:7,bivari:[5,7],bivariate_mi:0,bivariate_pid:0,bivariate_t:0,bmi:5,bte:5,comparison:[4,8],content:[0,9],cpu:2,data:[0,1,7],decomposit:7,depend:7,document:9,dynam:[7,8],embed:7,embedding_optimization_ais_rudelt:0,entropi:5,estim:[2,7],estimators_jidt:0,estimators_mpi:0,estimators_multivariate_pid:0,estimators_opencl:0,estimators_pid:0,estimators_python:0,estimators_rudelt:0,gpu:2,helper:3,histori:7,idtxl:[0,9,10],idtxl_except:0,idtxl_io:0,idtxl_util:0,indic:9,infer:[5,6,8],inform:[2,5,7],jidt:2,mine:6,mmi:5,modul:[0,3],mpi:2,mte:5,multivari:[5,7],multivariate_mi:0,multivariate_pid:0,multivariate_t:0,mutual:5,network:[4,5,6,8],network_analysi:0,network_comparison:0,network_infer:0,neural:7,node:[7,8],opencl:2,optim:7,packag:[0,9],partial:7,pid:[2,7],postprocess:6,python:2,result:[0,8],s:9,signific:6,single_process_analysi:0,spike:7,stat:[0,3],storag:7,subgraph:6,submodul:0,tabl:9,theoret:2,transfer:5,util:3,visualise_graph:0,welcom:9}}) \ No newline at end of file +Search.setIndex({docnames:["idtxl","idtxl_data_class","idtxl_estimators","idtxl_helper","idtxl_network_comparison","idtxl_network_inference","idtxl_postprocessing","idtxl_process_analysis","idtxl_results_class","index","modules"],envversion:{"sphinx.domains.c":2,"sphinx.domains.changeset":1,"sphinx.domains.citation":1,"sphinx.domains.cpp":4,"sphinx.domains.index":1,"sphinx.domains.javascript":2,"sphinx.domains.math":2,"sphinx.domains.python":3,"sphinx.domains.rst":2,"sphinx.domains.std":2,"sphinx.ext.viewcode":1,sphinx:56},filenames:["idtxl.rst","idtxl_data_class.rst","idtxl_estimators.rst","idtxl_helper.rst","idtxl_network_comparison.rst","idtxl_network_inference.rst","idtxl_postprocessing.rst","idtxl_process_analysis.rst","idtxl_results_class.rst","index.rst","modules.rst"],objects:{"":[[0,0,0,"-","idtxl"]],"idtxl.active_information_storage":[[0,1,1,"","ActiveInformationStorage"]],"idtxl.active_information_storage.ActiveInformationStorage":[[0,2,1,"","analyse_network"],[0,2,1,"","analyse_single_process"]],"idtxl.bivariate_mi":[[0,1,1,"","BivariateMI"]],"idtxl.bivariate_mi.BivariateMI":[[0,2,1,"","analyse_network"],[0,2,1,"","analyse_single_target"]],"idtxl.bivariate_pid":[[0,1,1,"","BivariatePID"]],"idtxl.bivariate_pid.BivariatePID":[[0,2,1,"","analyse_network"],[0,2,1,"","analyse_single_target"]],"idtxl.bivariate_te":[[0,1,1,"","BivariateTE"]],"idtxl.bivariate_te.BivariateTE":[[0,2,1,"","analyse_network"],[0,2,1,"","analyse_single_target"]],"idtxl.data":[[0,1,1,"","Data"]],"idtxl.data.Data":[[0,3,1,"","data"],[0,2,1,"","generate_logistic_maps_data"],[0,2,1,"","generate_mute_data"],[0,2,1,"","generate_var_data"],[0,2,1,"","get_realisations"],[0,2,1,"","get_seed"],[0,2,1,"","get_state"],[0,2,1,"","n_realisations"],[0,2,1,"","n_realisations_repl"],[0,2,1,"","n_realisations_samples"],[0,2,1,"","permute_replications"],[0,2,1,"","permute_samples"],[0,2,1,"","set_data"],[0,2,1,"","slice_permute_replications"],[0,2,1,"","slice_permute_samples"]],"idtxl.embedding_optimization_ais_Rudelt":[[0,1,1,"","OptimizationRudelt"]],"idtxl.embedding_optimization_ais_Rudelt.OptimizationRudelt":[[0,2,1,"","analyse_auto_MI"],[0,2,1,"","check_inputs"],[0,2,1,"","compute_CIs"],[0,2,1,"","get_R_tot"],[0,2,1,"","get_auto_MI"],[0,2,1,"","get_bootstrap_history_dependence"],[0,2,1,"","get_embeddings"],[0,2,1,"","get_embeddings_that_maximise_R"],[0,2,1,"","get_history_dependence"],[0,2,1,"","get_information_timescale_tau_R"],[0,2,1,"","get_past_range"],[0,2,1,"","get_set_of_scalings"],[0,2,1,"","get_temporal_depth_T_D"],[0,2,1,"","optimize"],[0,2,1,"","optimize_single_run"],[0,2,1,"","remove_subresults_single_process"]],"idtxl.estimators_Rudelt":[[0,1,1,"","RudeltAbstractEstimator"],[0,1,1,"","RudeltAbstractNSBEstimator"],[0,1,1,"","RudeltBBCEstimator"],[0,1,1,"","RudeltNSBEstimatorSymbolsMI"],[0,1,1,"","RudeltPluginEstimatorSymbolsMI"],[0,1,1,"","RudeltShufflingEstimator"]],"idtxl.estimators_Rudelt.RudeltAbstractEstimator":[[0,2,1,"","get_median_number_of_spikes_per_bin"],[0,2,1,"","get_multiplicities"],[0,2,1,"","get_past_range"],[0,2,1,"","get_raw_symbols"],[0,2,1,"","get_symbol_counts"],[0,2,1,"","get_window_delimiters"],[0,2,1,"","is_analytic_null_estimator"],[0,2,1,"","is_parallel"],[0,2,1,"","symbol_array_to_binary"],[0,2,1,"","symbol_binary_to_array"]],"idtxl.estimators_Rudelt.RudeltAbstractNSBEstimator":[[0,2,1,"","H1"],[0,2,1,"","alpha_ML"],[0,2,1,"","d2_log_rho"],[0,2,1,"","d2_log_rho_xi"],[0,2,1,"","d2_xi"],[0,2,1,"","d3_xi"],[0,2,1,"","d_log_rho"],[0,2,1,"","d_log_rho_xi"],[0,2,1,"","d_xi"],[0,2,1,"","get_beta_MAP"],[0,2,1,"","get_integration_bounds"],[0,2,1,"","log_likelihood_DP_alpha"],[0,2,1,"","nsb_entropy"],[0,2,1,"","rho"],[0,2,1,"","unnormalized_posterior"]],"idtxl.estimators_Rudelt.RudeltBBCEstimator":[[0,2,1,"","bayesian_bias_criterion"],[0,2,1,"","estimate"],[0,2,1,"","get_bbc_term"]],"idtxl.estimators_Rudelt.RudeltNSBEstimatorSymbolsMI":[[0,2,1,"","estimate"],[0,2,1,"","nsb_estimator"]],"idtxl.estimators_Rudelt.RudeltPluginEstimatorSymbolsMI":[[0,2,1,"","estimate"],[0,2,1,"","plugin_entropy"],[0,2,1,"","plugin_estimator"]],"idtxl.estimators_Rudelt.RudeltShufflingEstimator":[[0,2,1,"","estimate"],[0,2,1,"","get_H0_X_past_cond_X"],[0,2,1,"","get_H0_X_past_cond_X_eq_x"],[0,2,1,"","get_H_X_past_cond_X"],[0,2,1,"","get_H_X_past_uncond"],[0,2,1,"","get_P_X_past_cond_X"],[0,2,1,"","get_P_X_past_uncond"],[0,2,1,"","get_P_X_uncond"],[0,2,1,"","get_marginal_frequencies_of_spikes_in_bins"],[0,2,1,"","get_shuffled_symbol_counts"],[0,2,1,"","shuffling_MI"]],"idtxl.estimators_jidt":[[0,1,1,"","JidtDiscrete"],[0,1,1,"","JidtDiscreteAIS"],[0,1,1,"","JidtDiscreteCMI"],[0,1,1,"","JidtDiscreteMI"],[0,1,1,"","JidtDiscreteTE"],[0,1,1,"","JidtEstimator"],[0,1,1,"","JidtGaussian"],[0,1,1,"","JidtGaussianAIS"],[0,1,1,"","JidtGaussianCMI"],[0,1,1,"","JidtGaussianMI"],[0,1,1,"","JidtGaussianTE"],[0,1,1,"","JidtKraskov"],[0,1,1,"","JidtKraskovAIS"],[0,1,1,"","JidtKraskovCMI"],[0,1,1,"","JidtKraskovMI"],[0,1,1,"","JidtKraskovTE"],[0,4,1,"","common_estimate_surrogates_analytic"]],"idtxl.estimators_jidt.JidtDiscrete":[[0,2,1,"","estimate_surrogates_analytic"],[0,2,1,"","get_analytic_distribution"],[0,2,1,"","is_analytic_null_estimator"]],"idtxl.estimators_jidt.JidtDiscreteAIS":[[0,2,1,"","estimate"],[0,2,1,"","get_analytic_distribution"]],"idtxl.estimators_jidt.JidtDiscreteCMI":[[0,2,1,"","estimate"],[0,2,1,"","get_analytic_distribution"]],"idtxl.estimators_jidt.JidtDiscreteMI":[[0,2,1,"","estimate"],[0,2,1,"","get_analytic_distribution"]],"idtxl.estimators_jidt.JidtDiscreteTE":[[0,2,1,"","estimate"],[0,2,1,"","get_analytic_distribution"]],"idtxl.estimators_jidt.JidtEstimator":[[0,2,1,"","is_parallel"]],"idtxl.estimators_jidt.JidtGaussian":[[0,2,1,"","estimate_surrogates_analytic"],[0,2,1,"","get_analytic_distribution"],[0,2,1,"","is_analytic_null_estimator"]],"idtxl.estimators_jidt.JidtGaussianAIS":[[0,2,1,"","estimate"]],"idtxl.estimators_jidt.JidtGaussianCMI":[[0,2,1,"","estimate"],[0,2,1,"","get_analytic_distribution"]],"idtxl.estimators_jidt.JidtGaussianMI":[[0,2,1,"","estimate"]],"idtxl.estimators_jidt.JidtGaussianTE":[[0,2,1,"","estimate"]],"idtxl.estimators_jidt.JidtKraskov":[[0,2,1,"","is_analytic_null_estimator"]],"idtxl.estimators_jidt.JidtKraskovAIS":[[0,2,1,"","estimate"]],"idtxl.estimators_jidt.JidtKraskovCMI":[[0,2,1,"","estimate"]],"idtxl.estimators_jidt.JidtKraskovMI":[[0,2,1,"","estimate"]],"idtxl.estimators_jidt.JidtKraskovTE":[[0,2,1,"","estimate"]],"idtxl.estimators_mpi":[[0,1,1,"","MPIEstimator"]],"idtxl.estimators_mpi.MPIEstimator":[[0,2,1,"","estimate"],[0,2,1,"","estimate_surrogates_analytic"],[0,2,1,"","is_analytic_null_estimator"],[0,2,1,"","is_parallel"]],"idtxl.estimators_multivariate_pid":[[0,1,1,"","SxPID"]],"idtxl.estimators_multivariate_pid.SxPID":[[0,2,1,"","estimate"],[0,2,1,"","is_analytic_null_estimator"],[0,2,1,"","is_parallel"]],"idtxl.estimators_opencl":[[0,1,1,"","OpenCLKraskov"],[0,1,1,"","OpenCLKraskovCMI"],[0,1,1,"","OpenCLKraskovMI"]],"idtxl.estimators_opencl.OpenCLKraskov":[[0,2,1,"","is_analytic_null_estimator"],[0,2,1,"","is_parallel"]],"idtxl.estimators_opencl.OpenCLKraskovCMI":[[0,2,1,"","estimate"]],"idtxl.estimators_opencl.OpenCLKraskovMI":[[0,2,1,"","estimate"]],"idtxl.estimators_pid":[[0,1,1,"","SydneyPID"],[0,1,1,"","TartuPID"]],"idtxl.estimators_pid.SydneyPID":[[0,2,1,"","estimate"],[0,2,1,"","is_analytic_null_estimator"],[0,2,1,"","is_parallel"]],"idtxl.estimators_pid.TartuPID":[[0,2,1,"","estimate"],[0,2,1,"","is_analytic_null_estimator"],[0,2,1,"","is_parallel"]],"idtxl.estimators_python":[[0,1,1,"","PythonKraskovCMI"]],"idtxl.estimators_python.PythonKraskovCMI":[[0,2,1,"","estimate"],[0,2,1,"","is_analytic_null_estimator"],[0,2,1,"","is_parallel"]],"idtxl.idtxl_exceptions":[[0,5,1,"","AlgorithmExhaustedError"],[0,5,1,"","BROJA_2PID_Exception"],[0,5,1,"","JidtOutOfMemoryError"],[0,4,1,"","package_missing"]],"idtxl.idtxl_io":[[0,4,1,"","export_brain_net_viewer"],[0,4,1,"","export_networkx_graph"],[0,4,1,"","export_networkx_source_graph"],[0,4,1,"","import_fieldtrip"],[0,4,1,"","import_matarray"],[0,4,1,"","load_json"],[0,4,1,"","load_pickle"],[0,4,1,"","save_json"],[0,4,1,"","save_pickle"]],"idtxl.idtxl_utils":[[0,4,1,"","argsort_descending"],[0,4,1,"","autocorrelation"],[0,4,1,"","calculate_mi"],[0,4,1,"","combine_discrete_dimensions"],[0,4,1,"","conflicting_entries"],[0,4,1,"","discretise"],[0,4,1,"","discretise_max_ent"],[0,4,1,"","equal_dicts"],[0,4,1,"","print_dict"],[0,4,1,"","remove_column"],[0,4,1,"","remove_row"],[0,4,1,"","separate_arrays"],[0,4,1,"","sort_descending"],[0,4,1,"","standardise"],[0,4,1,"","swap_chars"],[0,1,1,"","timeout"]],"idtxl.idtxl_utils.timeout":[[0,2,1,"","timeout_handler"]],"idtxl.multivariate_mi":[[0,1,1,"","MultivariateMI"]],"idtxl.multivariate_mi.MultivariateMI":[[0,2,1,"","analyse_network"],[0,2,1,"","analyse_single_target"]],"idtxl.multivariate_pid":[[0,1,1,"","MultivariatePID"]],"idtxl.multivariate_pid.MultivariatePID":[[0,2,1,"","analyse_network"],[0,2,1,"","analyse_single_target"]],"idtxl.multivariate_te":[[0,1,1,"","MultivariateTE"]],"idtxl.multivariate_te.MultivariateTE":[[0,2,1,"","analyse_network"],[0,2,1,"","analyse_single_target"],[0,2,1,"","getit"]],"idtxl.network_analysis":[[0,1,1,"","NetworkAnalysis"]],"idtxl.network_analysis.NetworkAnalysis":[[0,3,1,"","current_value"],[0,2,1,"","resume_checkpoint"],[0,3,1,"","selected_vars_full"],[0,3,1,"","selected_vars_sources"],[0,3,1,"","selected_vars_target"]],"idtxl.network_comparison":[[0,1,1,"","NetworkComparison"]],"idtxl.network_comparison.NetworkComparison":[[0,2,1,"","calculate_link_te"],[0,2,1,"","compare_between"],[0,2,1,"","compare_links_within"],[0,2,1,"","compare_within"]],"idtxl.network_inference":[[0,1,1,"","NetworkInference"],[0,1,1,"","NetworkInferenceBivariate"],[0,1,1,"","NetworkInferenceMI"],[0,1,1,"","NetworkInferenceMultivariate"],[0,1,1,"","NetworkInferenceTE"]],"idtxl.results":[[0,1,1,"","AdjacencyMatrix"],[0,1,1,"","DotDict"],[0,1,1,"","Results"],[0,1,1,"","ResultsMultivariatePID"],[0,1,1,"","ResultsNetworkAnalysis"],[0,1,1,"","ResultsNetworkComparison"],[0,1,1,"","ResultsNetworkInference"],[0,1,1,"","ResultsPID"],[0,1,1,"","ResultsSingleProcessAnalysis"],[0,1,1,"","ResultsSingleProcessRudelt"]],"idtxl.results.AdjacencyMatrix":[[0,2,1,"","add_edge"],[0,2,1,"","add_edge_list"],[0,2,1,"","get_edge_list"],[0,2,1,"","n_edges"],[0,2,1,"","n_nodes"],[0,2,1,"","print_matrix"]],"idtxl.results.Results":[[0,2,1,"","combine_results"]],"idtxl.results.ResultsMultivariatePID":[[0,2,1,"","get_single_target"]],"idtxl.results.ResultsNetworkAnalysis":[[0,2,1,"","get_single_target"],[0,2,1,"","get_target_sources"],[0,3,1,"","targets_analysed"]],"idtxl.results.ResultsNetworkComparison":[[0,2,1,"","get_adjacency_matrix"],[0,2,1,"","get_single_target"],[0,2,1,"","get_target_sources"],[0,2,1,"","print_edge_list"]],"idtxl.results.ResultsNetworkInference":[[0,2,1,"","get_adjacency_matrix"],[0,2,1,"","get_source_variables"],[0,2,1,"","get_target_delays"],[0,2,1,"","print_edge_list"]],"idtxl.results.ResultsPID":[[0,2,1,"","get_single_target"]],"idtxl.results.ResultsSingleProcessAnalysis":[[0,2,1,"","get_significant_processes"],[0,2,1,"","get_single_process"],[0,3,1,"","processes_analysed"]],"idtxl.results.ResultsSingleProcessRudelt":[[0,2,1,"","get_single_process"],[0,3,1,"","processes_analysed"]],"idtxl.single_process_analysis":[[0,1,1,"","SingleProcessAnalysis"]],"idtxl.stats":[[0,4,1,"","ais_fdr"],[0,4,1,"","check_n_perm"],[0,4,1,"","max_statistic"],[0,4,1,"","max_statistic_sequential"],[0,4,1,"","max_statistic_sequential_bivariate"],[0,4,1,"","mi_against_surrogates"],[0,4,1,"","min_statistic"],[0,4,1,"","network_fdr"],[0,4,1,"","omnibus_test"],[0,4,1,"","syn_shd_against_surrogates"],[0,4,1,"","unq_against_surrogates"]],"idtxl.visualise_graph":[[0,4,1,"","plot_mute_graph"],[0,4,1,"","plot_network"],[0,4,1,"","plot_network_comparison"],[0,4,1,"","plot_selected_vars"]],idtxl:[[0,0,0,"-","active_information_storage"],[0,0,0,"-","bivariate_mi"],[0,0,0,"-","bivariate_pid"],[0,0,0,"-","bivariate_te"],[0,0,0,"-","data"],[0,0,0,"-","embedding_optimization_ais_Rudelt"],[0,0,0,"-","estimators_Rudelt"],[0,0,0,"-","estimators_jidt"],[0,0,0,"-","estimators_mpi"],[0,0,0,"-","estimators_multivariate_pid"],[0,0,0,"-","estimators_opencl"],[0,0,0,"-","estimators_pid"],[0,0,0,"-","estimators_python"],[0,0,0,"-","idtxl_exceptions"],[0,0,0,"-","idtxl_io"],[0,0,0,"-","idtxl_utils"],[0,0,0,"-","multivariate_mi"],[0,0,0,"-","multivariate_pid"],[0,0,0,"-","multivariate_te"],[0,0,0,"-","network_analysis"],[0,0,0,"-","network_comparison"],[0,0,0,"-","network_inference"],[0,0,0,"-","results"],[0,0,0,"-","single_process_analysis"],[0,0,0,"-","stats"],[0,0,0,"-","visualise_graph"]]},objnames:{"0":["py","module","Python module"],"1":["py","class","Python class"],"2":["py","method","Python method"],"3":["py","property","Python property"],"4":["py","function","Python function"],"5":["py","exception","Python exception"]},objtypes:{"0":"py:module","1":"py:class","2":"py:method","3":"py:property","4":"py:function","5":"py:exception"},terms:{"0":[0,1,2,3,4,5,6,7,8],"00001":[0,7],"005":[0,7],"00561":[0,7],"00629":[0,7],"0068910":0,"00706":[0,7],"00792":[0,7],"00889":[0,7],"00998":[0,7],"01":[0,7],"010":[0,5],"0109462":[0,1],"01119":[0,7],"01256":[0,7],"01409":[0,7],"01581":[0,7],"016":[0,7],"01774":[0,7],"01991":[0,7],"02233":[0,7],"025":[0,7],"02506":[0,7],"0262":[0,5],"02812":[0,7],"03":6,"03155":[0,7],"03356":[0,7],"0354":[0,7],"03972":[0,7],"04":[0,7],"04456":[0,7],"05":[0,3,4,5,7],"051112":[0,5,7],"0561":[0,7],"06295":[0,7],"066138":[0,2],"07063":[0,7],"07924":[0,7],"08891":[0,7],"09976":[0,7],"0s":[0,7],"1":[0,1,2,3,5,6,7,8],"10":[0,1,2,5,6,7],"100":[0,5,7],"1000":[0,1,7],"10000":[0,1,6],"1004":[0,7],"1007":[0,1,5],"1016":[0,7],"1024":[0,2],"11":[0,2,6],"1101":6,"1103":[0,5,7],"11194":[0,7],"11936":[0,7],"12559":[0,7],"1371":[0,1],"14":[0,1,7],"14092":[0,7],"15":[0,3,5,7],"15479":[0,7],"15811":[0,7],"16":[0,2,7],"17":[0,7],"17741":[0,7],"19":[0,2],"19905":[0,7],"1995":[0,8],"1d":[0,2,7],"1e":[0,2],"1s":[0,6,7],"1st":[0,1],"1th":[0,1],"2":[0,1,2,3,5,6,7,8],"20":[0,2,7],"200":[0,2,3,5,7],"2000":[0,2,5],"2001":[0,1],"2002":[0,3,7],"2004":[0,2],"2010":[0,7],"2011":[0,5,7],"2012":[0,2,5,7],"2013":0,"2014":[0,1,2,7],"2015":6,"2017":[0,2],"2018":[0,2],"2020":[0,7],"2021":[0,6,7],"208":[0,2,7],"2161":[0,2,7],"2183":[0,2,7],"21th":6,"22334":[0,7],"23342":[0,7],"25":[0,3,7],"250":[0,7],"25059":[0,7],"2515":[0,7],"25594":[0,7],"27":[0,7],"271":[0,2],"28117":[0,7],"2d":[0,2,3],"2pid":[0,2],"3":[0,1,2,5,6,7,8],"30":[0,5],"3000":[0,1],"31548":[0,7],"3389":[0,7],"3390":[0,2,7],"35397":[0,7],"37":6,"39":[0,2,7],"39716":[0,7],"4":[0,1,2,3,5,7,8],"40919":[0,7],"44563":[0,7],"45":[0,5,6],"45625":[0,7],"461":[0,2,5],"463":[0,1],"464":[0,5],"467050v1":6,"474":[0,1],"5":[0,1,5,7,8],"500":[0,3,4,5,7],"5000":[0,1],"50594":[0,7],"530":[0,2],"53973":[0,7],"54":[0,2,7],"56101":[0,7],"58114":[0,7],"6":[0,1,2,3,7],"60":[0,7],"62946":[0,7],"63":[0,7],"67":[0,5],"69":[0,2],"7":[0,1,7,8],"70627":[0,7],"725":6,"734":6,"77407":[0,7],"79245":[0,7],"8":[0,1,2,7],"81171":[0,7],"83":[0,5,7],"84":[0,1],"85":[0,2,5],"870":[0,3],"878":[0,3],"88914":[0,7],"9":[0,1],"97":[0,7],"97164":[0,7],"99054":[0,7],"99763":[0,7],"\u03ba":[0,7,8],"\u03c41":[0,7,8],"abstract":[0,2,7],"boolean":[0,2],"break":[0,2],"case":[0,6,7],"class":[0,2,3,4,5,6,7,9],"default":[0,1,2,3,4,5,6,7,8],"do":0,"export":0,"final":[0,2,5,7],"float":[0,1,2,3,4,5,6,7,8],"function":[0,1,2,5,6,7,9],"g\u00f6ttingen":[0,7],"import":[0,7],"int":[0,1,2,3,4,5,6,7,8],"long":[0,7],"new":[0,1,2,7],"null":[0,2],"return":[0,1,2,3,4,5,6,7,8],"true":[0,1,2,3,4,5,6,7,8],"v\u00f6gler":[0,7],"var":[0,1,2],"while":[0,1,2,7],A:[0,1,2,3,4,5,6,7,8],As:6,At:6,For:[0,1,2,3,5,6,7],If:[0,1,2,3,4,6,7,9],In:[0,6,7],It:[0,6],ONE:[0,1],One:[0,6,7],The:[0,2,3,4,5,6,7,9],There:[0,8],These:[0,7],To:[0,2,3,5,7,8],__main__:[0,2],__name__:[0,2],_create_surrogate_t:[0,3],_process0:[0,7],_single_target:[0,8],ab:[0,7,8],about:[0,4,7,8],abov:[0,2,7],absenc:[0,7],absolut:[0,8],abzing:[0,2],accept:[0,1],access:[0,8],accord:[0,1,3,6],achiev:6,acm:6,across:[0,3,6,7],actic:[0,2],activ:[0,2,3,8,9],active_information_storag:[7,9,10],activeinformationstorag:[0,7],actual:[0,1,2,3,6],ad:[0,2,5,7,8],adapt:[0,3],add:[0,2,5,6,7],add_condit:[0,5,7],add_edg:0,add_edge_list:0,addit:[0,1,7],addition:[0,7,8],adjac:[0,6,8],adjacency_matrix:0,adjacencymatrix:[0,8],advanc:[0,7],after:[0,3],again:[0,2,5,6],against:[0,3],ai:[0,2,3,8,9],ais_fdr:[0,3,7],ais_pval:[0,8],ais_sign:[0,8],ais_tot:[0,7,8],aka:[0,7],al:6,albert:[0,2],alg_num:[0,2],algorithm:[0,2,6,8,9],algorithm_num:[0,2],algorithmexhaustederror:[0,3],all:[0,1,2,3,4,5,6,7,8],allow:[0,1,2,3],alon:[0,2],along:[0,3],alph1:[0,2],alph2:[0,2],alph:[0,2,7],alph_s1:[0,2,7],alph_s2:[0,2,7],alph_t:[0,2,7],alpha:[0,3,4,5,6,7,8],alpha_:[0,5,7],alpha_comp:[0,4],alpha_fdr:[0,3],alpha_max_seq:[0,3],alpha_max_stat:[0,3],alpha_mi:[0,3,8],alpha_min_stat:[0,3],alpha_ml:[0,7],alpha_omnibu:[0,3],alphabet:[0,2],alphabet_s:[0,7],alphabet_size_past:[0,7],alphc:[0,2],alreadi:[0,2,5],also:[0,6],alwai:[0,2],among:[0,6,7],an:[0,1,2,3,4,7,8],analys:[0,1,8],analyse_auto_mi:[0,7,8],analyse_network:[0,5,7],analyse_single_process:[0,7],analyse_single_target:[0,5,7],analysi:[0,1,2,3,4,5,8,9],analysis_setup:[0,3],analyt:[0,2,7],analyticnulldistribut:[0,2],analyz:[0,7],ani:[0,1,6],anoth:[0,2],append:6,appli:[0,2,3,7],approach:0,ar:[0,1,2,3,4,5,6,7,8],arang:[0,1],arbitrari:[0,2],arg:[0,1,2,3,4,5,6,7,8],argsort_descend:[0,3],argument:[0,2,6],around:[0,7],arrai:[0,1,2,3,4,5,6,7,8],array_nam:0,arxiv:[0,7],assign:[0,4],associ:6,assum:[0,2],assumpt:[0,3,7],astyp:[0,7],attain:[0,7],attemp:[0,2],attempt:[0,2],attribut:[0,1,3,5,6,7,8],auto:[0,1,7],auto_mi:[0,7,8],auto_mi_bin_s:[0,7,8],auto_mi_bin_size_set:[0,7],auto_mi_delai:[0,7,8],auto_mi_max_delai:[0,7,8],autocorrel:[0,3,7,8],autodetect:0,automat:[0,7],autoregress:0,avail:[0,2,7,8],averag:[0,2,7],avg:[0,8],avoid:[0,2],ax:[0,1],axi:[0,3],ay:[0,2,7],b:[0,4,6,8],baccala:[0,1],back:[0,1,7],base:[0,2,3,4,5,7],basi:0,basic:[0,3],bayesian:[0,7],bayesian_bias_criterion:[0,7],bbc:[0,7],bbc_term:[0,7],bbc_toler:[0,7],becaus:[0,1,2,3,6,7],becker:[0,7],been:[0,2,4,7,8],beer:[0,7],befor:[0,2,3,8],being:9,below:[0,7],benjamini:[0,8],bertsching:[0,2,7],best:6,beta:[0,7],beta_map:[0,7],between:[0,2,3,4,5,6,7],bia:[0,7],bialek:[0,7],bias:[0,7],big:[0,3],bigger:[0,3],bin:[0,2,3,7,8],bin_siz:[0,7],binari:[0,7,8],biol:[0,1],biologi:[0,7],biorxiv:6,bit:[0,2],bivar:0,bivari:[0,2,3,8,9],bivariate_mi:[5,9,10],bivariate_pid:[7,9,10],bivariate_t:[5,9,10],bivariatemi:[0,5],bivariatepid:[0,7],bivariatet:[0,5],block:[0,1,7],block_siz:[0,1],bmi:9,bnv:0,bool:[0,1,2,3,4,5,6,7,8],bootstrap:[0,7],bootstrap_ci_percentile_hi:[0,7],bootstrap_ci_percentile_lo:[0,7],bootstrap_ci_use_sd:[0,7],borgwardt:6,both:[0,2,3,4,7,8],bound:[0,2,7],boundari:[0,2],brain:0,brainnet:0,broja:[0,2],broja_2pid:[0,2],broja_2pid_except:0,bte:9,build:6,c:[0,3],calcclass:[0,2],calcul:[0,1,2,3,4,5,6,7],calculate_link_t:[0,4],calculate_mi:[0,3],call:[0,2,3,5,6,7,8],cambridg:[0,7],can:[0,1,2,3,4,5,6,7,8],candid:[0,3,5,7],candidate_set:[0,3],cannot:[0,2,3],carri:6,caus:0,causal:[0,5,7],cf:[0,7],chang:[0,2],channel:0,charact:[0,1,3],characterist:[0,7,8],check:[0,2,3,4,6,7],check_input:[0,7],check_n_perm:[0,3],checkpoint:[0,5,7],child:[0,2,7],choic:6,choos:[0,3],chosen:[0,1,7],chunk:[0,2,7],ci:[0,7],circular:[0,1],ckp:0,close:[0,7],cmi:[0,2,4,5,7],cmi_diff_ab:[0,8],cmi_estim:[0,4,5,7,8],code:[0,2,6],coding_list:6,coeffici:[0,1,3],coefficient_matric:[0,1],coher:[0,1],collect:[0,4],color:0,column:[0,3],com:[0,2,7],combin:[0,1,3,6,7],combine_discrete_dimens:[0,3],combine_result:0,come:0,common:[0,2],common_estimate_surrogates_analyt:[0,2],commonli:0,comp:[0,5],compar:[0,1,3,4,8],compare_between:[0,4],compare_links_within:[0,4],compare_within:[0,4],comparison:[0,3,6,9],complet:[0,3,7],complex:[0,2,6,7],comprehens:0,comput:[0,2,5,6,7],compute_ci:[0,7],computecombinedvalu:[0,3],concaten:[0,2],concentr:[0,7],concept:[0,1],condit:[0,2,3,4,5,6,7,8],conditionalmutualinfocalculatormultivariategaussian:[0,2],cone:[0,2],cone_solv:[0,2],confer:6,confid:[0,2,7],conflict:0,conflicting_entri:[0,3],connect:[0,4,5,8],connectom:0,consequ:[0,7],consid:[0,3,5,6],consider:[0,7],consist:[0,1,5,6],consol:[0,2,3,4,5,7,8],constant:[0,3],construct:[0,5],contain:[0,2,3,5,7,8],content:[6,10],context:[0,2,3],continu:[0,2,3],contribut:[0,7],control:[0,2,4],conveni:[0,8],converg:[0,2],convert:[0,6,7],coordin:0,copi:[0,3],corr:[0,3],correct:[0,3,5,6,7,8],correct_by_target:[0,3],correl:[0,3],correspond:[0,1,2,6,8],could:0,count:[0,1,2,6,7],count_discord:6,count_discordants_wylight:6,count_subgraph:6,count_subgraph_wylight:6,coupl:[0,1],cpu:[0,9],crash:[0,5,7],creat:[0,1,3,4,6,7],creation:[0,1,3,4,5,7],criterion:[0,7,8],critic:[0,3,4,5,7],current:[0,1,2,3,5,6,7,8],current_min_freq:6,current_min_p:6,current_symbol_arrai:[0,7],current_valu:[0,1,5,7,8],cybern:[0,1],d2_log_rho:[0,7],d2_log_rho_xi:[0,7],d2_xi:[0,7],d3_xi:[0,7],d:[0,1,2,3,7,8],d_log_rho:[0,7],d_log_rho_xi:[0,7],d_xi:[0,7],data:[2,3,4,5,6,8,9,10],data_1:[0,1],data_2:[0,1],data_a:[0,4],data_b:[0,4],data_format:6,data_mut:[0,1],data_new:[0,1],data_properti:[0,8],data_set_a:[0,4],data_set_b:[0,4],data_spiketim:[0,7],de:[0,5],debug:[0,2,7],decod:6,decode_adjac:6,decomposit:[0,2,8,9],decreas:[0,2],decrement:[0,2],defin:[0,2,3],degre:[0,2,3],delai:[0,2,7,8],delet:[0,7],delimit:[0,7],delta:[0,7],denomin:[0,3],depend:[0,1,4,6,8,9],dependent_var:[0,7],depth:[0,5,7,8],deriv:[0,3,7],descend:[0,3],describ:[0,2,5,6,7,8],descric:[0,2],descript:[0,5,7],design:6,desir:6,detail:[0,2,3,4,5,7,8],detect:[0,3,4,5,7],determin:[0,1,6,7,8],determine_tarone_factor:6,deviat:[0,1,3,7],devic:[0,2],df:[0,3],dict:[0,1,2,3,4,5,6,7,8],dict_1:[0,3],dict_2:[0,3],dictionari:[0,2,3,7,8],dietterich:[0,7],diff_ab:[0,8],differ:[0,1,2,3,4,6,7,8],differenti:[0,2,7],digraph:0,dim_ord:[0,1],dimens:[0,1,2,3],dimension:[0,1,3],direct:[0,1,2,6],dirichlet:[0,7],discard:[0,7],discord:6,discoveri:[0,3,6],discret:[0,1,2,3],discretis:[0,2,3],discretise_max_:[0,3],discretise_method:[0,2],discretisemaxentropi:[0,3],discuss:9,disk:[0,5,7],displai:0,display_edge_label:0,distanc:[0,2],distribut:[0,2,3,6,7,8],divid:[0,3],docstr:[0,5,7,8],document:[0,2,3,4,5,7,8],doe:[0,2,3,6],doi:[0,1,2,5,7],don:0,done:[0,3,7],dot:[0,8],dotdict:[0,7],doubl:[0,2],down:[0,3],drawn:[0,7],due:[0,2,7],duplic:0,dure:[0,7],dynam:[0,1,2,9],e109462:[0,1],e16042161:[0,2,7],e68910:0,e:[0,1,2,3,4,5,6,7,8],each:[0,1,2,3,4,5,6,7,8],easi:[0,7],east:6,eco:[0,2],edg:[0,8],editor:[0,7],effect:[0,4,5,7,8],effici:[0,6,7],either:[0,2,3,5,6,7,8],els:[0,7],emb:[0,7],embed:[0,1,2,4,5,8,9],embedding_number_of_bins_set:[0,7],embedding_optimization_ais_rudelt:[7,9,10],embedding_past_range_set:[0,7],embedding_scaling_exponent_set:[0,7],embedding_step_s:[0,7],empir:[0,2],empti:[0,1,6],enabl:[0,5,7],encod:6,encode_adjac:6,engin:0,enough:[0,3,6],enter:[0,8],entri:[0,1,3,4,6,7,8],entropi:[0,1,2,3,7,8,9],enumerate_frequent_graph:6,enumerate_frequent_subgraph:6,enumerate_significant_subgraph:6,ep:[0,7],eq:[0,1],equal:[0,2,3],equal_dict:[0,3],equiv:[0,7],equival:[0,7,8],err:0,error:[0,2],essenti:[0,7],est:[0,2],establish:[0,1],estim:[0,1,3,4,5,6,8,9],estimate_parallel:[0,2],estimate_surrogates_analyt:[0,2,7],estimation_method:[0,7,8],estimators_:[0,4,5,7],estimators_jidt:[2,9,10],estimators_mpi:[2,9,10],estimators_multivariate_pid:[9,10],estimators_opencl:[2,9,10],estimators_pid:[2,7,9,10],estimators_python:[2,9,10],estimators_rudelt:[7,9,10],et:6,evalu:6,even:6,evenli:[0,3],event:[0,7],everi:[0,2,5,7],ex:[0,2,3],exact:[0,2],exampl:[0,1,3,4,5,7],except:[0,3],exception_messag:[0,3],exclud:[0,5],exist:[0,1],expect:[0,1,7],experiment:[0,4],explan:0,expon:[0,7,8],exponenti:[0,2],export_brain_net_view:0,export_networkx_graph:0,export_networkx_source_graph:0,extend:6,extend_mcnemar:6,extend_wi:6,extend_wy_light:6,extend_wy_light_mcnemar:6,extend_wy_mcnemar:6,extens:[0,5,6,7],extract:[0,7],f:[0,1,6,7],face:[0,4],factor:[0,6,7],fae:[0,1,5,7],fall:[0,1],fals:[0,1,2,3,5,7],far:6,fast:[0,2,6],faster:[0,3],fdr:[0,3,5,8],fdr_constant:[0,3],fdr_correct:[0,5,7],femal:[0,4],fewer:[0,8],field:[0,3],fieldtrip:0,fieldtriptoolbox:0,fifth:0,figur:0,file:[0,5,7],file_nam:0,file_path:0,file_vers:0,filename_ckp:[0,5,7],fill:6,find:[0,2,5,7],fire:[0,7,8],firing_r:[0,7,8],first:[0,1,2,4,6,7,8],first_bin_s:[0,7],fit:[0,1],five:[0,1],fix:[0,1],fl:6,fninf:[0,7],follow:[0,1,7],forc:[0,5,7],form:[0,1,6],format:[0,1,2,7],forth:[0,3],forward:[0,2],found:[0,5,7],four:[0,4,5],fourth:0,free:[0,5],freedom:[0,3],freq:6,frequenc:[0,6,7],frequent:6,frequent_graph:6,frequent_subgraph:6,from:[0,1,2,3,4,5,7,8],front:[0,2,7],fsampl:0,ft_datatype_raw:0,ft_struct_nam:0,full:[0,3,5,6],further:[0,4,5,6,7],futur:[0,2],g:[0,1,2,5,7,8],galusk:[0,7],gaussian:[0,1,2],gc:0,gener:[0,1,3,8],generate_coding_list:6,generate_logistic_maps_data:[0,1],generate_min_p_t:6,generate_mute_data:[0,1,5,7],generate_p_t:6,generate_var_data:[0,1],genoves:[0,3],get:[0,1,7,8],get_adjacency_matrix:[0,8],get_analytic_distribut:[0,2],get_as_list:[0,7],get_auto_mi:[0,7],get_bbc_term:[0,7],get_beta_map:[0,7],get_bootstrap_history_depend:[0,7],get_edge_list:0,get_embed:[0,7],get_embeddings_that_maximise_r:[0,7],get_h0_x_past_cond_x:[0,7],get_h0_x_past_cond_x_eq_x:[0,7],get_h_x_past_cond_x:[0,7],get_h_x_past_uncond:[0,7],get_history_depend:[0,7],get_information_timescale_tau_r:[0,7],get_integration_bound:[0,7],get_marginal_frequencies_of_spikes_in_bin:[0,7],get_max_r_t:[0,7],get_median_number_of_spikes_per_bin:[0,7],get_multipl:[0,7],get_p_x_past_cond_x:[0,7],get_p_x_past_uncond:[0,7],get_p_x_uncond:[0,7],get_past_rang:[0,7],get_r_thresh:[0,7],get_r_tot:0,get_raw_symbol:[0,7],get_realis:[0,1],get_realisations_symbol:[0,7],get_se:[0,1],get_set_of_sc:[0,7],get_shannon_entropi:[0,7],get_shuffled_symbol_count:[0,7],get_significant_process:[0,8],get_single_process:[0,8],get_single_target:[0,8],get_source_vari:[0,8],get_stat:[0,1],get_symbol_count:[0,7],get_target_delai:[0,8],get_target_sourc:[0,8],get_temporal_depth_t_d:[0,7],get_window_delimit:[0,7],getit:0,ghahramani:[0,7],github:[0,2,3,7,9],give:[0,2],given:[0,1,2,3,6,7,8],glass:0,goal:6,good:0,googl:9,gpu:[0,9],gpuid:[0,2],granger:[0,5,7],graph:[0,7],graph_typ:6,grassberg:[0,2],greedi:0,group:[0,6,7,9],groupa:6,groupa_network:6,groupb:6,groupb_network:6,guard:[0,2],guess:[0,7],gutknecht:[0,6,7],h0_sh_x_past_cond_x:[0,7],h0_x_past_cond_x:[0,7],h1:[0,7],h:[0,2,7],h_0:[0,7],h_spike:[0,7,8],h_uncond:[0,7],h_x_past_cond_x:[0,7],h_x_past_uncond:[0,7],ha:[0,1,2,3,4,5,6,7,8],halv:[0,2],handl:0,happen:6,hard:[0,2],have:[0,1,2,3,4,5,7,8,9],hde:[0,7],hdestim:[0,7],hdf5:0,he:0,head:9,hehlotler:[0,3],hellother:[0,3],helper:9,henc:[0,2,5],here:[0,2,7],heurist:[0,7],high:[0,2,8],higher:0,highest:[0,3,8],highest_protocol:0,histori:[0,2,8,9],history_depend:[0,7,8],history_sourc:[0,2],history_target:[0,2],hit:[0,2],hold:[0,8],home:0,hommel:6,hopefulli:0,host:9,hous:[0,4],how:[0,2,7],howev:[0,2],http:[0,1,2,3,5,6,7],human:0,hypothesi:[0,2],i:[0,2,3,4,5,6,7,8],i_1:[0,3],i_2:[0,3],i_corr:[0,7],i_list:0,i_plugin:[0,7],id:[0,2,8],identifi:[0,3,6,8],idtxl:[1,2,3,4,5,6,7,8],idtxl_checkpoint:[0,5,7],idtxl_except:[9,10],idtxl_io:[9,10],idtxl_util:[3,9,10],idx:[0,1,5,7],idx_al:[0,3],idx_list:[0,1],idx_realis:[0,1],idx_singl:[0,3],ie:[0,7],ignor:[0,2,6,7],imag:[0,7],immedi:6,implement:[0,1,2,4,6,7],implicitli:[0,1],import_fieldtrip:0,import_matarrai:0,improv:[0,2],includ:[0,3,6,7,8],incom:[0,2],inconveni:0,increment:[0,2],ind:[0,1],indent:[0,3],independ:[0,2,4,7],index:[0,1,3,4,5,6,7,8,9],indic:[0,1,2,3,5,6,7,8],individu:[0,2,3,5,6,7,8],inf:6,infer:[0,3,4,7,9],infin:6,infinit:[0,2],influenc:[0,5,7],infodynam:[0,3],infor:[0,2],inform:[0,1,3,4,8,9],initi:[0,1,2,6],initial_st:[0,1],initialis:[0,1],inner:[0,2,5],input:[0,2,3,6,7,8],ins:[0,7],insid:0,inspect:[0,7],instanc:[0,1,2,3,4,5,6,7,8],instanti:[0,2],instead:[0,1,2,3,5,7],institut:[0,5],int32:[0,2],intact:[0,1],integ:[0,1,6,7],integr:[0,7],interact:[0,8],intermedi:[0,2],intern:[0,6],interv:[0,7],intial:6,introduc:6,io:[0,3],is_analytic_null_estim:[0,2,7],is_parallel:[0,2,7],issu:9,iter:[0,5,7],ith:6,its:[0,2,7,8],j:[0,2,3,5,7],j_list:0,java:[0,2],jidt:[0,3,9],jidtdiscret:[0,2],jidtdiscreteai:[0,2],jidtdiscretecmi:[0,2],jidtdiscretemi:[0,2],jidtdiscretet:[0,2],jidtestim:[0,2],jidtgaussian:[0,2],jidtgaussianai:[0,2],jidtgaussiancmi:[0,2],jidtgaussianmi:[0,2],jidtgaussiant:[0,2],jidtkraskov:[0,2],jidtkraskovai:[0,2],jidtkraskovcmi:[0,2,5,7],jidtkraskovmi:[0,2],jidtkraskovt:[0,2],jidtoutofmemoryerror:[0,2],joint:[0,2,3,4,5],joseph:[0,2],jost:[0,2,7],journal:[0,1],jpackag:[0,2],jpype:[0,2],json:0,k1:[0,7],k:[0,1,6,7],k_rt:6,kappa:[0,7],kasenburg:6,keep:[0,1,2],kei:[0,3,7,8],keyword:[0,8],km:6,knn:[0,2],knn_finder:[0,2],knowledg:6,kraskov:[0,2],kraskov_k:[0,2],ksg:[0,2],kwarg:[0,2],l:[0,1,5,6,7],label:0,labl:0,lag:[0,1,2,5,6,7,8],lag_mi:[0,2],lags_pid:[0,7],larger:[0,2,6],last:[0,3,7],later:0,latter:[0,2],lattic:[0,8],lazar:[0,3],le:0,least:6,len:[0,3],lenght:[0,7],length:[0,1,2,3,7,8],lett:[0,2,5],level:[0,2,3,4,5,6,7],light:6,like:[0,7,8],likelihood:[0,7],limit:[0,2],lindner:[0,5,7],line:[0,4],linear:[0,1],linearli:[0,7],link:[0,3,4,5,6,8],link_a:[0,4],link_b:[0,4],link_count:6,list:[0,1,3,4,5,6,7,8],lizier:[0,2,3,5,7],llinar:6,load:[0,7],load_json:0,load_pickl:0,local:[0,1,2,7],local_valu:[0,2],locat:[0,6,7],log:[0,7],log_likelihood_dp_alpha:[0,7],logarithm:[0,7],logic:[0,3],logical_xor:[0,7],logist:[0,1],longer:0,look:[0,8,9],lookup:6,loop:[0,2],loos:[0,1],lopez:6,lower:[0,7],lowest:[0,3],m:[0,2,5,6,7],ma:[0,7],machin:[0,2],made:[0,3],mai:[0,2,8],main:[0,2,6,7],make:[0,2,3,7],makkeh:[0,2,7],male:[0,4],manag:[0,3],mani:[0,2],manual:0,map:[0,1,3,6,7],margin:[0,7],marginal_prob:[0,7],marinazzo:[0,1],marx:[0,7],mass:[0,2],mat:0,match:[0,4],matlab:[0,1],matplotlib:0,matric:[0,1,6],matrix:[0,1,6,8],matrixutil:[0,3],max:[0,1,2,3,5],max_depth:6,max_ent:[0,2],max_it:[0,2,7],max_lag:[0,5,7],max_lag_sourc:[0,5],max_lag_target:[0,5],max_p:[0,8],max_p_lag:[0,8],max_seq:[0,5],max_shift:[0,1],max_stat:[0,5,7],max_statist:[0,3],max_statistic_sequenti:[0,3],max_statistic_sequential_bivari:[0,3],max_t:[0,8],max_te_lag:[0,8],max_unsuc_swaps_row_parm:[0,2,7],max_work:[0,2],maxim:[0,7],maximis:[0,5,7],maximum:[0,1,2,3,5,6,7,8],mcnemar:6,mean:6,measur:[0,2,5,6,7,8],median:[0,7],mem:[0,2],memori:[0,6],messag:[0,3],method:[0,1,2,3,4,5,6,7,8],mi:[0,2,3,5,7,8],mi_against_surrog:[0,3],mi_omnibu:[0,5],mi_sign_sourc:[0,5],michael:[0,7],mikhail:[0,2],min:[0,3],min_first_bin_s:[0,7],min_freq:6,min_lag:[0,5],min_lag_sourc:[0,5],min_p_value_t:6,min_stat:[0,5,7],min_statist:[0,3],min_step_for_sc:[0,7],mine:[0,8,9],minimum:[0,3,5,6,7],minimum_p_valu:6,minu:[0,7],misinform:0,miss:0,mit:[0,7],mk:[0,7],mmi:9,mni:0,mni_coord:0,model:[0,1,5,7],modul:[2,4,5,7,8,9,10],moment:[0,7],montalto:[0,1],more:[0,2,5,6,7,9],most:[0,6,7],mpg:[0,5],mpi4pi:[0,2],mpi:[0,9],mpiestim:[0,2],mpiexec:[0,2],mte:9,multi:[0,3],multinomi:[0,7],multipl:[0,1,2,4,6,7,8],multipli:[0,3],multivar:0,multivari:[0,1,2,3,4,8,9],multivariate_mi:[5,9,10],multivariate_pid:[7,9,10],multivariate_t:[5,9,10],multivariatemi:[0,5],multivariatepid:[0,7],multivariatet:[0,3,5,8],must:[0,1,2,3,7],mute:[0,1],mutual:[0,2,3,7,9],n:[0,1,2,3,6,7],n_a:6,n_b:6,n_chunk:[0,2],n_discrete_bin:[0,2],n_edg:0,n_node:[0,8],n_perm:[0,2,3],n_perm_:[0,5,7],n_perm_comp:[0,4],n_perm_max_seq:[0,3,5],n_perm_max_stat:[0,3,5,7],n_perm_mi:[0,3],n_perm_min_stat:[0,3,5,7],n_perm_omnibu:[0,3,5],n_process:[0,1,8],n_realis:[0,1,8],n_realisations_repl:[0,1],n_realisations_sampl:[0,1],n_replic:[0,1],n_sampl:[0,1],name:[0,5,7],nat:[0,2],ndarrai:[0,2],nearest:[0,2],nearli:[0,2],necessari:[0,1,7],necessarili:[0,7],need:[0,2,7],neighborhood:[0,2],neighbour:[0,2],nemenman:[0,7],network:[0,1,3,7,9],network_a:[0,4],network_analysi:[5,7,9,10],network_b:[0,4],network_comparison:[4,9,10],network_fdr:[0,3,5,8],network_infer:[9,10],network_set_a:[0,4],network_set_b:[0,4],networkanalysi:0,networkcomparison:[0,4],networkinfer:0,networkinferencebivari:0,networkinferencemi:0,networkinferencemultivari:0,networkinferencet:0,networkx:0,neural:[0,1,6,9],neuroimag:[0,3],neuroinf:[0,7],neuron:[0,7,8],neurophysiolog:0,neurosci:[0,5],never:[0,2],next:[0,2],nichol:[0,3],nitrc:0,node:[0,3,5,9],node_color:0,node_s:0,nois:[0,1,2],noise_level:[0,2],noise_std:[0,1],nollo:[0,5,7],non:[0,1,3,5,7],none:[0,1,2,3,6,7],nonessenti:[0,7],nonlinear:[0,5,7],nonneg:[0,7],nonuniform:[0,5,7],normal:[0,7],normalis:[0,1,2,7,8],notat:[0,7,8],note:[0,1,2,3,4,5,7,8],novel:[0,1],now:[0,2,3],np:[0,1,2,7],nsb:[0,7],nsb_entropi:[0,7],nsb_estim:[0,7],num_perm:6,num_rep:[0,2,7],num_testable_graph:6,num_thread:[0,2],number:[0,1,2,3,4,5,6,7,8],number_of_bins_d:[0,7],number_of_bootstrap:[0,7],number_of_bootstraps_nonessenti:[0,7],number_of_bootstraps_r_max:[0,7],number_of_bootstraps_r_tot:[0,7],number_of_delai:[0,7],number_of_sc:[0,7],number_of_symbol:[0,7],numbin:[0,3],numer:[0,2],numpi:[0,1,2,3,4,5,6,7,8],o:[0,2],obj:0,object:[0,1,2,3,4,7],observ:[0,2,4,5,6,7],obtain:[0,3,6,7,8],occur:[0,6,7],occurr:[0,6,7],offer:0,often:[0,6,7],olbrich:[0,2,7],old:[0,3],omnibu:[0,3,5],omnibus_pv:0,omnibus_sign:0,omnibus_t:0,omnibus_test:[0,3],onc:[0,2,6],one:[0,2,3,4,6,7,8],onli:[0,1,2,3,5,6,7],opencl:[0,9],openclkraskov:[0,2],openclkraskovcmi:[0,2],openclkraskovmi:[0,2],oppos:[0,2],opt_first_bin_s:[0,7,8],opt_number_of_bins_d:[0,7,8],opt_scaling_k:[0,7,8],optim:[0,2,8,9],optimizationrudelt:[0,7],optimize_single_run:[0,7],option:[0,1,2,3,4,5,7,8],order:[0,1,3],org:[0,1,2,5,6,7],orgin:[0,1],orgini:[],origin:[0,1,2,3,6,7],other:[0,3,5,6,7],otherwis:[0,1,3,7],our:[0,2],out:[0,6],outer:[0,2],outofmemoryexcept:0,output:[0,2,4,5,7,8],output_path:[0,7],output_prefix:[0,7],outsid:[0,2],over:[0,1,2,3,4,5,7],overwrit:[0,1,2],own:[0,5,7],p0_sh_x_past_cond_x:[0,7],p:[0,1,2,3,5,6,7,8],p_0:[0,7],p_valu:[0,3,6],p_value_t:6,p_values_corr:6,p_x_past_cond_x:[0,7],p_x_past_uncond:[0,7],p_x_uncond:[0,7],packag:[2,10],package_miss:0,pad:[0,2],page:[0,3,9],pair:[0,5,6,7],papaxantho:6,paper:[0,1,3],parallel:[0,1,2,3,7],paramet:[0,2,3,4,5,7],parent:[0,2,7,8],part:[0,4,6],partial:[0,1,2,3,8,9],partial_information_decomposit:[0,3],partic:[0,2],particip:[0,4],particular:[0,3,6],partit:[0,3],pass:[0,2,3,4,7],past:[0,2,3,4,5,7,8],past_range_t:[0,7],past_symbol:[0,7],past_symbol_arrai:[0,7],past_symbol_count:[0,7],path:[0,7],patient:[0,4],pattern:[0,1,6],pdf:[0,5],peak:[0,7],per:[0,1,2,6,7],percentil:[0,7],perform:[0,2,3,5,7],performancetip:[0,3],perm:6,perm_rang:[0,1],perm_set:[0,1],perm_typ:[0,1],permut:[0,1,2,3,4,5,6,7],permute_in_tim:[0,3,4,5,7],permute_repl:[0,1],permute_sampl:[0,1,4,5,7],permutet:[],perspect:[0,2],phy:[0,2,5,7],physrev:[0,5,7],physrevlett:[0,5],pickl:0,pid:[0,3,8,9],pid_analysi:[0,7],pid_calc_nam:[0,7],pid_estim:[0,7],pipa:[0,5],pl00007990:[0,1],planck:[0,5],platform:[0,2],plo:[0,1,7],plot:[0,7],plot_mute_graph:0,plot_network:0,plot_network_comparison:0,plot_selected_var:0,plug:[0,2,7],plugin:[0,7],plugin_entropi:[0,7],plugin_estim:[0,7],pmi2:[0,2],point:[0,1,2,3,7,8],pointless:[0,3],pointwis:0,pone:[0,1],porta:[0,5,7],possibl:[0,2,3,6,7],posterior:[0,7],posteriori:[0,7],postprocess:[0,8,9],potenti:[0,5],power:[0,3],practic:[0,2],predict:[0,3],prefix:[0,7],preprint2012_25:[0,5],preprint:[0,5],present:[0,3],preserv:[0,7],press:[0,7],pretti:[0,3],previous:[0,5,7],priesemann:[0,7],principl:[0,7],print:[0,2,3,6,8],print_dict:[0,3],print_edge_list:[0,8],print_matrix:0,printer:[0,3],prior:[0,7],probabl:[0,2,7],problem:9,proc:[0,1],procedur:6,proceed:6,process:[0,1,2,3,4,5,6,7,8],process_set:[0,7],processes_analys:[0,8],produc:[0,7],product:[0,7],program:[0,2],project:0,prokopenko:[0,2,7],properti:[0,1,2,8],propos:[0,1,2],protocol:0,provid:[0,1,2,3,4,7,8],prune:[0,3,5,7],ps:[0,1,7],psr:[0,1],ptw:[0,8],put:[0,3],pval:[0,8],pvalu:[0,5,7,8],pvalue_omnibu:[0,5],pvalues_sign_sourc:[0,5],pypi:[0,2],python3:0,python:[0,3,7,9],pythonkraskovcmi:[0,2],qualit:0,quantifi:[0,2,7],quantil:6,quantiti:[0,2,7],r:[0,1,2,3,5,7,8],r_max:[0,7],r_nsb:[0,7],r_plugin:[0,7],r_tot:[0,7,8],rais:[0,1,2,3],randint:[0,7],random:[0,1,2,7,8],rang:[0,1,2,3,7],rank:[0,2,3],rate:[0,3,7,8],rauh:[0,2,7],raw:[0,1,2,3,4,5,7],raw_symbol:[0,7],re:[0,3,7],reach:6,read:[0,7],realis:[0,1,2,3,5,7,8],realiz:[0,2],rebas:[0,7],recogn:0,recommend:[0,7],record:[0,4,7,8],recording_length:[0,7,8],recurs:[0,2,6],reduc:[0,7],redund:[0,8],refer:[0,1,2,3,5,7],regard:[0,8],regress:[0,1],regular:0,rel:[0,7],relat:[0,7],relationship:[0,2],relev:[0,3,5,6,7],remain:[0,3],reman:[0,3],remanin:[0,3],remov:[0,2,3],remove_column:[0,3],remove_row:[0,3],remove_subresults_single_process:[0,7],repeat:[0,3],repetit:[0,1],repl:[0,1],replic:[0,1,2,3,4,5,7],report:[0,9],repres:[0,1,2,3,6,7,8],represent:[0,7],request:[0,1,3],requir:[0,2,3,6],res_network:[0,8],res_pid:[0,8],resampl:[0,7],reshap:[0,1],respect:[0,2,5],respons:[0,7],restrict:[0,3],result:[1,2,3,4,5,6,7,9,10],resultsa:6,resultsb:6,resultsmultivariatepid:[0,7,8],resultsnetworkanalysi:0,resultsnetworkcomparison:[0,4,8],resultsnetworkinfer:[0,3,5,8],resultspid:[0,7,8],resultssingleprocessanalysi:[0,3,7,8],resultssingleprocessrudelt:[0,7,8],resum:[0,5,7],resume_checkpoint:[0,5,7],retriev:[0,5,7],return_averaged_r:[0,7],return_calc:[0,2],return_count:[0,2],rev:[0,2,5,7],reveal:[0,7],revisit:[0,7],rho:[0,7],rlz:[0,8],rng_seed:[0,2],robot:[0,2],round:[0,3],routin:0,row:[0,2,3],rtot:[0,7,8],rubinov:[0,5],rudelt:[0,7,8],rudeltabstractestim:[0,7],rudeltabstractnsbestim:[0,7],rudeltbbcestim:[0,7],rudeltnsbestimatorsymbolsmi:[0,7],rudeltpluginestimatorsymbolsmi:[0,7],rudeltshufflingestim:[0,7],run:[0,1,2],runtim:[0,2],s10827:[0,5],s1:[0,2,7],s1_unq:[0,8],s2:[0,2,7],s2_unq:[0,8],s3:[0,7],s:[0,1,2,3,4,5,6,7],same:[0,1,2,3,4,5,6,7],sameshima:[0,1],sampl:[0,1,2,3,5,6,7,8],save:[0,2,3,6,7],save_json:0,save_pickl:0,scale:[0,7,8],scaling_k:[0,7],scheme:[0,1],schreiber:[0,2,5],sci:[0,2,7],scipi:[0,3],scipy_kdtre:[0,2],script:[0,2],search:[0,2,5,7,9],second:[0,2,3,4,6,7,8],see:[0,2,3,4,5,7,8],seed:[0,1,2],select:[0,3,4,5,7,8],selected_sources_pv:0,selected_sources_t:0,selected_var:[0,8],selected_vars_ful:[0,5,7],selected_vars_sourc:[0,5,8],selected_vars_target:[0,5,8],self:[0,6,7],separ:[0,3,5,7],separate_arrai:[0,3],sequenc:[0,2],sequenti:[0,3],seri:[0,1,2],set:[0,1,2,3,4,5,6,7,8],set_data:[0,1],setup:[0,3],sever:[0,1],sh:[0,7],shafe:[0,7],shape:[0,3],share:[0,2,3,7,8],shd_s1_s2:[0,8],shift:[0,1],shorter:[0,2],should:[0,2,3,7],show:[0,7],shuffl:[0,1,3,4,5,7],shuffling_mi:[0,7],siam:6,siamintern:6,side:[0,4],sigkdd:6,sign:[0,7],sign_ominbu:[0,5],sign_sourc:0,signal:0,signific:[0,3,4,5,7,8,9],significant_graph:6,significantli:[0,3],significantsubgraphmin:6,similar:[0,2,7,8],simple_depth_first:6,simple_depth_fist:6,simplic:[0,7],simplifi:[0,7],simul:[0,1,7],singl:[0,3,4,5,7,8],single_process_analysi:[9,10],singleprocessanalysi:0,singleton:0,size:[0,1,2,6,7,8],sklearn_balltre:[0,2],sklearn_kdtre:[0,2],slice:[0,1],slice_permute_repl:[0,1],slice_permute_sampl:[0,1],slid:[0,7],slurm:[0,2],slurm_ntask:[0,2],small:[0,1,3],smaller:[0,2,3,6],smallest:[0,6,7,8],sn:0,so:[0,2,3,6,7],soft:[0,2],softwar:0,solver:[0,2],solver_arg:[0,2],some:[0,2,7],soon:6,sort:[0,3],sort_descend:[0,3],sortind:[0,3],sourc:[0,1,2,3,4,5,6,7,8],source_1:[0,8],source_2:[0,8],source_i:[0,8],source_set:[0,5],source_target_delai:[0,2],sources_test:0,space:[0,2,5,7],spawn:[0,2],specif:[0,8],specifi:[0,1,5,6,7,8],spike:[0,8,9],spike_tim:[0,7],spikes_in_window:[0,7],split:[0,2],spr:[0,1],squar:[0,1],squeez:0,srun:[0,2],stage:[0,2],stai:[0,1],stamp:0,stand:[],standard:[0,1,3,7],standardis:[0,1,2,3,8],start:[0,1],stat:[5,7,8,9,10],state:[0,1,2,5,7,8],statis:[],statist:[0,1,3,4,5,7,8],statistic_omnibu:[0,5],statistic_sign_sourc:[0,5],stats_typ:[0,4],step:[0,5,6,7],stochast:[0,1,8],stoegbauer:[0,2],stop:[0,3,6],storag:[0,2,3,8,9],store:[0,1,8],str:[0,1,2,4,5,7,8],strategi:[0,1],string:[0,1,3,5,6,7,8],structur:[0,1],studi:[0,2],style:0,subgraph:[0,8,9],subject:[0,4,6],submodul:[9,10],subplot:0,subset:[0,3,7],success:6,suffici:[0,1,7],sugiyama:6,sum:[0,7],sum_i:[0,3],summar:[0,7],summari:6,supergraph:6,support:[0,2,7],sure:[0,2],surrog:[0,1,2,3,4,5,7,8],surrogate_distribut:[0,8],swap:[0,1,2,3],swap_char:[0,3],sxpid:[0,7],sydneypid:[0,2,7],symbol:[0,7],symbol_arrai:[0,7],symbol_array_to_binari:[0,7],symbol_binari:[0,7],symbol_binary_to_arrai:[0,7],symbol_block_length:[0,7],symbol_count:[0,7],symmetr:[0,7],syn_s1_s2:[0,8],syn_shd_against_surrog:[0,3],synergist:[0,2,3,7,8],system:[0,2,7],t:[0,2,3,5,7,8],t_0:[0,7],t_d:[0,7,8],t_max:[0,7],tabl:[0,3,6],tail:[0,4],tail_comp:[0,4],take:[0,1,7],taken:[0,2,3],target1:[0,7],target:[0,2,3,4,5,6,7,8],target_r:[0,7],targets_analys:[0,8],taron:6,tartupid:[0,2],task:[0,2,4,7],tau:[0,2,7],tau_1:[0,7],tau_r:[0,7,8],tau_sourc:[0,2,5],tau_target:[0,2,5],td:[0,7,8],te:[0,2,3,4,5,7,8],te_max_candid:[0,3],te_min_candid:[0,3],technic:[0,2],techniqu:[0,5,7],tempor:[0,1,2,5,7,8],term:[0,7],test:[0,1,2,3,4,5,6,7,8],testabl:6,text:0,th:6,than:[0,2,3,6,7],thei:[0,2,6,7],theiler:[0,2],theiler_t:[0,2],them:0,theoret:[0,3,8,9],theori:0,therefor:[0,7],thereof:[0,7],thi:[0,1,2,3,4,5,6,7,8],third:[0,2,7],those:0,thread:[0,2,3],three:[0,1,2,5,7],threshold:[0,3],through:[0,5,7],thu:[0,1,2,7],time:[0,1,2,3,4,5,6,7,8],timeout:[0,3],timeout_dur:[0,3],timeout_handl:0,timescal:[0,7,8],timescale_minimum_past_rang:[0,7],tmax:[0,7],to_be_extend:6,todo:[0,7],togeth:[0,7],toggl:[0,4,5,7],toler:[0,7],too:[0,1,2],tool:[0,7],toolbox:[0,1],toolkit:[0,2],tot:[0,7,8],total:[0,1,2,6,7,8],toward:[0,7],tracker:9,train:[0,7,8],tranfer:[0,8],transfer:[0,1,2,3,4,8,9],treat:[0,5],trial:[0,1,2],trigger:[0,3],triplet:6,tupl:[0,1,3,5,6,7,8],tutori:9,two:[0,1,2,3,4,5,6,7,8],type:[0,1,2,4,7],typeerror:[0,1],typic:[0,7],u:[0,1],unbias:[0,7],uncorrect:[0,6,8],uncorrel:[0,1],under:[0,2,4,6,7],understand:[0,7],undirect:6,unequ:[0,3],uniform:[0,2,5,7],union:[0,6,8],union_indic:6,uniqu:[0,2,3,7,8],unit:[0,4],univari:[0,3],unnorm:[0,7],unnormalized_posterior:[0,7],unq_against_surrog:[0,3],unsuccess:[0,2],until:[0,2],unweight:[0,8],up:[0,4,6,7],updat:6,upon:0,upper:[0,2,7],us:[0,1,2,3,4,5,6,7,8],usag:[0,2],use_al:[0,2],user:[0,9],usiz:[0,2],util:[0,9],utilis:[0,2],utl:[0,7],v4:0,v6:0,v7:0,v:[0,7],valid:0,valu:[0,1,2,3,4,5,6,7,8],var1:[0,2],var2:[0,2],vari:[0,2],variabl:[0,1,2,3,4,5,7,8],variou:[0,1],vars_count:[0,8],vector:[0,1,3],verbos:[0,2,4,5,6,7],version:[0,2,6],via:[0,1,2,5,6,7,8],vicent:[0,2,5],viewer:0,virtualis:[0,2],visual:[0,7],visualis:0,visualise_graph:[9,10],visul:[0,7],vitrualis:[0,2],vs:[0,4],vstack:[0,7],w:[0,7],wa:[0,1,2,4,7,8],wai:[0,8],wait:[0,3],wang:0,want:[0,9],warn:0,we:[0,1,7],weight:[0,8],weight_typ:0,well:[0,2,3,7],were:[0,5,7,8],westfal:6,westfall_young:6,westfall_young_light:6,what:[0,7],when:[0,1,2,3,5,7],where:[0,1,2,3,5,6,7,8],whether:[0,7,8],which:[0,1,2,3,4,6,7],whole:[0,3,4,5,7],whose:[0,7],wibral:[0,5,6,7],wiki:[0,3,9],william:[0,7],window:[0,2,7],wise:[0,1],within:[0,1,4,6],without:[0,5,7],workaround:[0,2],worker:[0,2],wrapper:[0,2],write:[0,5,7],write_ckp:[0,5,7],written:0,wrt:[0,5,7,8],www:[0,5,6],wy:6,wy_algorithm:6,wy_level_light:6,wy_light:6,x:[0,1,2,3,7],x_past:[0,7],xi:[0,7],xia:0,xl:0,y:[0,2,7],yield:[0,7,8],you:[0,9],young:6,your:[0,2],z:[0,1,2,3,7,8],zero:[0,7],zomaya:[0,2,7]},titles:["idtxl package","The Data Class","Information theoretic estimators","Helper functions","Network comparison","Algorithms for network inference","Postprocessing of inferred networks","Algorithms for the analysis of node dynamics","The Results Class","Welcome to IDTxl\u2019s documentation!","idtxl"],titleterms:{"class":[1,8],"function":3,The:[1,8],activ:7,active_information_storag:0,ai:7,algorithm:[5,7],analysi:7,bivari:[5,7],bivariate_mi:0,bivariate_pid:0,bivariate_t:0,bmi:5,bte:5,comparison:[4,8],content:[0,9],cpu:2,data:[0,1,7],decomposit:7,depend:7,document:9,dynam:[7,8],embed:7,embedding_optimization_ais_rudelt:0,entropi:5,estim:[2,7],estimators_jidt:0,estimators_mpi:0,estimators_multivariate_pid:0,estimators_opencl:0,estimators_pid:0,estimators_python:0,estimators_rudelt:0,gpu:2,helper:3,histori:7,idtxl:[0,9,10],idtxl_except:0,idtxl_io:0,idtxl_util:0,indic:9,infer:[5,6,8],inform:[2,5,7],jidt:2,mine:6,mmi:5,modul:[0,3],mpi:2,mte:5,multivari:[5,7],multivariate_mi:0,multivariate_pid:0,multivariate_t:0,mutual:5,network:[4,5,6,8],network_analysi:0,network_comparison:0,network_infer:0,neural:7,node:[7,8],opencl:2,optim:7,packag:[0,9],partial:7,pid:[2,7],postprocess:6,python:2,result:[0,8],s:9,signific:6,single_process_analysi:0,spike:7,stat:[0,3],storag:7,subgraph:6,submodul:0,tabl:9,theoret:2,transfer:5,util:3,visualise_graph:0,welcom:9}}) \ No newline at end of file From 4a926b843d918676728ca72e09d62373fd522b56 Mon Sep 17 00:00:00 2001 From: Patricia Wollstadt Date: Mon, 29 Jan 2024 17:00:39 +0100 Subject: [PATCH 21/34] Fix network FDR for empty networks We previously filtered targets by significant Ombinus tests. Without this filtering, we may get networks that are empty (i.e., no target has any incoming links). For empty networks, the Omnibus no. permutations is None if not explicitely specified by the user. The FDR test crashes if this case is not explicitely handled when testing for empty networks When filtering by Omnibus sign., checking the length of the p-values to be tested was sufficient. Now we also have to check if all Ombines tests were skipped due to no included source variables by testing if Ombinus n_perm is None for all p-values. --- idtxl/stats.py | 4 ++-- test/test_results.py | 22 ++++++++++++---------- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/idtxl/stats.py b/idtxl/stats.py index 7ee3852..de7e84f 100644 --- a/idtxl/stats.py +++ b/idtxl/stats.py @@ -196,7 +196,7 @@ def network_fdr(settings=None, *results): cands = cands + (results_comb._single_target[target].selected_vars_sources) n_perm = np.append(n_perm, results_comb.settings.n_perm_max_seq) - if pval.size == 0: + if pval.size == 0 or (n_perm == None).all(): # n_perm is None for empty networks, i.e., no omnibus test was run print("No links in final results ...") results_comb._add_fdr( fdr=None, @@ -210,7 +210,7 @@ def network_fdr(settings=None, *results): # If the number of permutations for calculating p-values for individual # variables is too low, return without performing any correction. - if (1 / min(n_perm)) > thresh[0]: + if (1 / min(i for i in n_perm if i is not None)) > thresh[0]: print( "WARNING: Number of permutations ('n_perm_max_seq') for at least one target is too low to allow for " f"FDR correction (FDR-threshold: {thresh[0]:.4f}, min. theoretically possible p-value: {1 / min(n_perm)})." diff --git a/test/test_results.py b/test/test_results.py index 616d499..3da5f86 100644 --- a/test/test_results.py +++ b/test/test_results.py @@ -1,21 +1,23 @@ """Test IDTxl results class.""" +import copy as cp +import itertools as it import os import pickle -import pytest from tempfile import TemporaryFile -import itertools as it -import copy as cp + import numpy as np -from idtxl.results import AdjacencyMatrix -from idtxl.multivariate_te import MultivariateTE -from idtxl.bivariate_te import BivariateTE -from idtxl.multivariate_mi import MultivariateMI +import pytest +from test_estimators_jidt import jpype_missing + from idtxl.bivariate_mi import BivariateMI -from idtxl.network_comparison import NetworkComparison +from idtxl.bivariate_te import BivariateTE from idtxl.data import Data from idtxl.estimators_jidt import JidtDiscreteCMI -from test_estimators_jidt import jpype_missing from idtxl.idtxl_utils import calculate_mi +from idtxl.multivariate_mi import MultivariateMI +from idtxl.multivariate_te import MultivariateTE +from idtxl.network_comparison import NetworkComparison +from idtxl.results import AdjacencyMatrix # Use common settings dict that can be used for each test settings = { @@ -457,8 +459,8 @@ def test_adjacency_matrix(): if __name__ == "__main__": - test_adjacency_matrix() test_console_output() + test_adjacency_matrix() test_results_network_inference() test_results_network_comparison() test_pickle_results() From 8c7122a496732c28d3d42f7c9a6568a70aa26c73 Mon Sep 17 00:00:00 2001 From: Patricia Wollstadt Date: Mon, 29 Jan 2024 17:04:07 +0100 Subject: [PATCH 22/34] Ensure network_analysis handles partially empty networks If single links have no significant inputs, i.e., no source variables are selected, no Omnibus test is performed. If the user did not specify the no. permutations for the Omnibus test, this parameter is not set. Ensure that this does not lead to problems when combining results across targets and one target has no Ombinus test settings, while the other one does. --- test/test_multivariate_te.py | 605 +++++++++++++++++++---------------- 1 file changed, 335 insertions(+), 270 deletions(-) diff --git a/test/test_multivariate_te.py b/test/test_multivariate_te.py index 2daaee6..e18c325 100644 --- a/test/test_multivariate_te.py +++ b/test/test_multivariate_te.py @@ -1,15 +1,16 @@ """Provide unit tests for multivariate TE estimation.""" -import pytest import itertools as it + import numpy as np -from idtxl.multivariate_te import MultivariateTE +import pytest +from test_checkpointing import _clear_ckp +from test_estimators_jidt import _get_gauss_data, jpype_missing +from test_results import _get_discrete_gauss_data + from idtxl.data import Data from idtxl.estimators_jidt import JidtDiscreteCMI, JidtKraskovTE -from test_estimators_jidt import jpype_missing -from test_results import _get_discrete_gauss_data -from test_checkpointing import _clear_ckp from idtxl.idtxl_utils import calculate_mi -from test_estimators_jidt import _get_gauss_data +from idtxl.multivariate_te import MultivariateTE SEED = 0 @@ -22,42 +23,50 @@ def test_gauss_data(): source = source[1:] source_uncorr = source_uncorr[1:] target = target[:-1] - data = Data(np.hstack((source, source_uncorr, target)), - dim_order='sp', normalise=False) + data = Data( + np.hstack((source, source_uncorr, target)), dim_order="sp", normalise=False + ) settings = { - 'cmi_estimator': 'JidtKraskovCMI', - 'n_perm_max_stat': 21, - 'n_perm_min_stat': 21, - 'n_perm_max_seq': 21, - 'n_perm_omnibus': 21, - 'max_lag_sources': 2, - 'min_lag_sources': 1} + "cmi_estimator": "JidtKraskovCMI", + "n_perm_max_stat": 21, + "n_perm_min_stat": 21, + "n_perm_max_seq": 21, + "n_perm_omnibus": 21, + "max_lag_sources": 2, + "min_lag_sources": 1, + } nw = MultivariateTE() - results = nw.analyse_single_target( - settings, data, target=2, sources=[0, 1]) - te = results.get_single_target(2, fdr=False)['te'][0] + results = nw.analyse_single_target(settings, data, target=2, sources=[0, 1]) + te = results.get_single_target(2, fdr=False)["te"][0] sources = results.get_target_sources(2, fdr=False) # Assert that only the correlated source was detected. - assert len(sources) == 1, 'Wrong no. inferred sources: {0}.'.format( - len(sources)) - assert sources[0] == 0, 'Wrong inferred source: {0}.'.format(sources[0]) + assert len(sources) == 1, "Wrong no. inferred sources: {0}.".format(len(sources)) + assert sources[0] == 0, "Wrong inferred source: {0}.".format(sources[0]) # Compare BivarateMI() estimate to JIDT estimate. Mimick realisations used # internally by the algorithm. - est = JidtKraskovTE({ - 'history_target': 1, - 'history_source': 1, - 'source_target_delay': 1, - 'normalise': False}) + est = JidtKraskovTE( + { + "history_target": 1, + "history_source": 1, + "source_target_delay": 1, + "normalise": False, + } + ) jidt_cmi = est.estimate(source=source, target=target) - print('Estimated MI: {0:0.6f}, estimated MI using JIDT core estimator: ' - '{1:0.6f} (expected: {2:0.6f}).'.format(te, jidt_cmi, expected_mi)) + print( + "Estimated MI: {0:0.6f}, estimated MI using JIDT core estimator: " + "{1:0.6f} (expected: {2:0.6f}).".format(te, jidt_cmi, expected_mi) + ) assert np.isclose(te, jidt_cmi, atol=0.005), ( - 'Estimated MI {0:0.6f} differs from JIDT estimate {1:0.6f} (expected: ' - 'MI {2:0.6f}).'.format(te, jidt_cmi, expected_mi)) - assert np.isclose(te, expected_mi, atol=0.05), ( - 'Estimated TE {0:0.6f} differs from expected TE {1:0.6f}.'.format( - te, expected_mi)) + "Estimated MI {0:0.6f} differs from JIDT estimate {1:0.6f} (expected: " + "MI {2:0.6f}).".format(te, jidt_cmi, expected_mi) + ) + assert np.isclose( + te, expected_mi, atol=0.05 + ), "Estimated TE {0:0.6f} differs from expected TE {1:0.6f}.".format( + te, expected_mi + ) @jpype_missing @@ -67,64 +76,83 @@ def test_return_local_values(): data = Data(seed=SEED) data.generate_mute_data(500, 5) settings = { - 'cmi_estimator': 'JidtKraskovCMI', - 'noise_level': 0, - 'local_values': True, # request calculation of local values - 'n_perm_max_stat': 21, - 'n_perm_min_stat': 21, - 'n_perm_max_seq': 21, - 'n_perm_omnibus': 21, - 'max_lag_sources': max_lag, - 'min_lag_sources': 4, - 'max_lag_target': max_lag} + "cmi_estimator": "JidtKraskovCMI", + "noise_level": 0, + "local_values": True, # request calculation of local values + "n_perm_max_stat": 21, + "n_perm_min_stat": 21, + "n_perm_max_seq": 21, + "n_perm_omnibus": 21, + "max_lag_sources": max_lag, + "min_lag_sources": 4, + "max_lag_target": max_lag, + } target = 3 sources = [0, 4] te = MultivariateTE() - results = te.analyse_single_target( - settings, data, target=target, sources=sources) - settings['local_values'] = False + results = te.analyse_single_target(settings, data, target=target, sources=sources) + settings["local_values"] = False results_avg = te.analyse_single_target( - settings, data, target=target, sources=sources) + settings, data, target=target, sources=sources + ) # Test if any sources were inferred. If not, return (this may happen # sometimes due to too few samples, however, a higher no. samples is not # feasible for a unit test). - if results.get_single_target(target, fdr=False)['te'] is None: + if results.get_single_target(target, fdr=False)["te"] is None: return - if results_avg.get_single_target(target, fdr=False)['te'] is None: + if results_avg.get_single_target(target, fdr=False)["te"] is None: return - lte = results.get_single_target(target, fdr=False)['te'] + lte = results.get_single_target(target, fdr=False)["te"] n_sources = len(results.get_target_sources(target, fdr=False)) - assert type(lte) is np.ndarray, ( - 'LTE estimation did not return an array of values: {0}'.format(lte)) - assert lte.shape[0] == n_sources, ( - 'Wrong dim (no. sources) in LTE estimate: {0}'.format(lte.shape)) - assert lte.shape[1] == data.n_realisations_samples((0, max_lag)), ( - 'Wrong dim (no. samples) in LTE estimate: {0}'.format(lte.shape)) - assert lte.shape[2] == data.n_replications, ( - 'Wrong dim (no. replications) in LTE estimate: {0}'.format(lte.shape)) + assert ( + type(lte) is np.ndarray + ), "LTE estimation did not return an array of values: {0}".format(lte) + assert ( + lte.shape[0] == n_sources + ), "Wrong dim (no. sources) in LTE estimate: {0}".format(lte.shape) + assert lte.shape[1] == data.n_realisations_samples( + (0, max_lag) + ), "Wrong dim (no. samples) in LTE estimate: {0}".format(lte.shape) + assert ( + lte.shape[2] == data.n_replications + ), "Wrong dim (no. replications) in LTE estimate: {0}".format(lte.shape) # Check if average and mean local values are the same. Test each source # separately. Inferred sources and variables may differ between the two # calls to analyse_single_target() due to low number of surrogates used in # unit testing. - te_single_link = results_avg.get_single_target(target, fdr=False)['te'] + te_single_link = results_avg.get_single_target(target, fdr=False)["te"] sources_local = results.get_target_sources(target, fdr=False) sources_avg = results_avg.get_target_sources(target, fdr=False) for s in list(set(sources_avg).intersection(sources_local)): i1 = np.where(sources_avg == s)[0][0] i2 = np.where(sources_local == s)[0][0] # Skip comparison if inferred variables differ between links. - vars_local = [v for v in results_avg.get_single_target(target, fdr=False).selected_vars_sources if v[0] == s] - vars_avg = [v for v in results.get_single_target(target, fdr=False).selected_vars_sources if v[0] == s] + vars_local = [ + v + for v in results_avg.get_single_target( + target, fdr=False + ).selected_vars_sources + if v[0] == s + ] + vars_avg = [ + v + for v in results.get_single_target(target, fdr=False).selected_vars_sources + if v[0] == s + ] if vars_local != vars_avg: continue - print('Compare average ({0:.4f}) and local values ({1:.4f}).'.format( - te_single_link[i1], np.mean(lte[i2, :, :]))) + print( + "Compare average ({0:.4f}) and local values ({1:.4f}).".format( + te_single_link[i1], np.mean(lte[i2, :, :]) + ) + ) assert np.isclose(te_single_link[i1], np.mean(lte[i2, :, :]), rtol=0.00005), ( - 'Single link average MI ({0:.6f}) and mean LMI ({1:.6f}) ' - ' deviate.'.format(te_single_link[i1], np.mean(lte[i2, :, :]))) + "Single link average MI ({0:.6f}) and mean LMI ({1:.6f}) " + " deviate.".format(te_single_link[i1], np.mean(lte[i2, :, :])) + ) @jpype_missing @@ -132,18 +160,18 @@ def test_multivariate_te_init(): """Test instance creation for MultivariateTE class.""" # Test error on missing estimator settings = { - 'n_perm_max_stat': 21, - 'n_perm_omnibus': 30, - 'max_lag_sources': 7, - 'min_lag_sources': 2, - 'max_lag_target': 5} + "n_perm_max_stat": 21, + "n_perm_omnibus": 30, + "max_lag_sources": 7, + "min_lag_sources": 2, + "max_lag_target": 5, + } nw = MultivariateTE() with pytest.raises(AssertionError): - nw.analyse_single_target( - settings=settings, data=Data(seed=SEED), target=1) + nw.analyse_single_target(settings=settings, data=Data(seed=SEED), target=1) # Test setting of min and max lags - settings['cmi_estimator'] = 'JidtKraskovCMI' + settings["cmi_estimator"] = "JidtKraskovCMI" data = Data(seed=SEED) data.generate_mute_data(n_samples=10, n_replications=5) @@ -151,70 +179,70 @@ def test_multivariate_te_init(): nw.analyse_single_target(settings=settings, data=data, target=1) # Valid: max lag sources smaller than max lag target - settings['max_lag_sources'] = 3 + settings["max_lag_sources"] = 3 nw.analyse_single_target(settings=settings, data=data, target=1) # Invalid: min lag sources bigger than max lag - settings['min_lag_sources'] = 8 - settings['max_lag_sources'] = 7 - settings['max_lag_target'] = 5 + settings["min_lag_sources"] = 8 + settings["max_lag_sources"] = 7 + settings["max_lag_target"] = 5 with pytest.raises(RuntimeError): nw.analyse_single_target(settings=settings, data=data, target=1) # Invalid: taus bigger than lags - settings['min_lag_sources'] = 2 - settings['max_lag_sources'] = 4 - settings['max_lag_target'] = 5 - settings['tau_sources'] = 10 + settings["min_lag_sources"] = 2 + settings["max_lag_sources"] = 4 + settings["max_lag_target"] = 5 + settings["tau_sources"] = 10 with pytest.raises(RuntimeError): nw.analyse_single_target(settings=settings, data=data, target=1) - settings['tau_sources'] = 1 - settings['tau_target'] = 10 + settings["tau_sources"] = 1 + settings["tau_target"] = 10 with pytest.raises(RuntimeError): nw.analyse_single_target(settings=settings, data=data, target=1) # Invalid: negative lags or taus - settings['min_lag_sources'] = 1 - settings['max_lag_target'] = 5 - settings['max_lag_sources'] = -7 - settings['tau_target'] = 1 + settings["min_lag_sources"] = 1 + settings["max_lag_target"] = 5 + settings["max_lag_sources"] = -7 + settings["tau_target"] = 1 with pytest.raises(RuntimeError): nw.analyse_single_target(settings=settings, data=data, target=1) - settings['max_lag_sources'] = 7 - settings['min_lag_sources'] = -4 + settings["max_lag_sources"] = 7 + settings["min_lag_sources"] = -4 with pytest.raises(RuntimeError): nw.analyse_single_target(settings=settings, data=data, target=1) - settings['min_lag_sources'] = 4 - settings['max_lag_target'] = -1 + settings["min_lag_sources"] = 4 + settings["max_lag_target"] = -1 with pytest.raises(RuntimeError): nw.analyse_single_target(settings=settings, data=data, target=1) - settings['max_lag_target'] = 5 - settings['tau_sources'] = -1 + settings["max_lag_target"] = 5 + settings["tau_sources"] = -1 with pytest.raises(RuntimeError): nw.analyse_single_target(settings=settings, data=data, target=1) - settings['tau_sources'] = 1 - settings['tau_target'] = -1 + settings["tau_sources"] = 1 + settings["tau_target"] = -1 with pytest.raises(RuntimeError): nw.analyse_single_target(settings=settings, data=data, target=1) # Invalid: lags or taus are no integers - settings['tau_target'] = 1 - settings['min_lag_sources'] = 1.5 + settings["tau_target"] = 1 + settings["min_lag_sources"] = 1.5 with pytest.raises(RuntimeError): nw.analyse_single_target(settings=settings, data=data, target=1) - settings['min_lag_sources'] = 1 - settings['max_lag_sources'] = 1.5 + settings["min_lag_sources"] = 1 + settings["max_lag_sources"] = 1.5 with pytest.raises(RuntimeError): nw.analyse_single_target(settings=settings, data=data, target=1) - settings['max_lag_sources'] = 7 - settings['tau_sources'] = 1.5 + settings["max_lag_sources"] = 7 + settings["tau_sources"] = 1.5 with pytest.raises(RuntimeError): nw.analyse_single_target(settings=settings, data=data, target=1) - settings['tau_sources'] = 1 - settings['tau_target'] = 1.5 + settings["tau_sources"] = 1 + settings["tau_target"] = 1.5 with pytest.raises(RuntimeError): nw.analyse_single_target(settings=settings, data=data, target=1) - settings['tau_target'] = 1 + settings["tau_target"] = 1 # Invalid: sources or target is no int with pytest.raises(RuntimeError): # no int @@ -226,17 +254,13 @@ def test_multivariate_te_init(): with pytest.raises(RuntimeError): # wrong type nw.analyse_single_target(settings=settings, data=data, target={}) with pytest.raises(RuntimeError): # negative - nw.analyse_single_target(settings=settings, data=data, target=0, - sources=-1) - with pytest.raises(RuntimeError): # negative - nw.analyse_single_target(settings=settings, data=data, target=0, - sources=[-1]) + nw.analyse_single_target(settings=settings, data=data, target=0, sources=-1) + with pytest.raises(RuntimeError): # negative + nw.analyse_single_target(settings=settings, data=data, target=0, sources=[-1]) with pytest.raises(RuntimeError): # not in data - nw.analyse_single_target(settings=settings, data=data, target=0, - sources=20) + nw.analyse_single_target(settings=settings, data=data, target=0, sources=20) with pytest.raises(RuntimeError): # not in data - nw.analyse_single_target(settings=settings, data=data, target=0, - sources=[20]) + nw.analyse_single_target(settings=settings, data=data, target=0, sources=[20]) @jpype_missing @@ -247,75 +271,85 @@ def test_multivariate_te_one_realisation_per_replication(): # This is easyer to assert/verify later. We also test data.get_realisations # this way. settings = { - 'cmi_estimator': 'JidtKraskovCMI', - 'n_perm_max_stat': 21, - 'max_lag_target': 5, - 'max_lag_sources': 5, - 'min_lag_sources': 4} + "cmi_estimator": "JidtKraskovCMI", + "n_perm_max_stat": 21, + "max_lag_target": 5, + "max_lag_sources": 5, + "min_lag_sources": 4, + } target = 0 data = Data(normalise=False, seed=SEED) n_repl = 10 n_procs = 2 - n_points = n_procs * (settings['max_lag_sources'] + 1) * n_repl - data.set_data(np.arange(n_points).reshape( - n_procs, settings['max_lag_sources'] + 1, n_repl), 'psr') + n_points = n_procs * (settings["max_lag_sources"] + 1) * n_repl + data.set_data( + np.arange(n_points).reshape(n_procs, settings["max_lag_sources"] + 1, n_repl), + "psr", + ) nw_0 = MultivariateTE() - nw_0._initialise(settings, data, 'all', target) - assert (not nw_0.selected_vars_full) - assert (not nw_0.selected_vars_sources) - assert (not nw_0.selected_vars_target) - assert ((nw_0._replication_index == np.arange(n_repl)).all()) - assert (nw_0._current_value == (target, max( - settings['max_lag_sources'], settings['max_lag_target']))) - assert (nw_0._current_value_realisations[:, 0] == - data.data[target, -1, :]).all() + nw_0._initialise(settings, data, "all", target) + assert not nw_0.selected_vars_full + assert not nw_0.selected_vars_sources + assert not nw_0.selected_vars_target + assert (nw_0._replication_index == np.arange(n_repl)).all() + assert nw_0._current_value == ( + target, + max(settings["max_lag_sources"], settings["max_lag_target"]), + ) + assert (nw_0._current_value_realisations[:, 0] == data.data[target, -1, :]).all() @jpype_missing def test_faes_method(): """Check if the Faes method is working.""" - settings = {'cmi_estimator': 'JidtKraskovCMI', - 'add_conditionals': 'faes', - 'max_lag_sources': 5, - 'min_lag_sources': 3, - 'max_lag_target': 7} + settings = { + "cmi_estimator": "JidtKraskovCMI", + "add_conditionals": "faes", + "max_lag_sources": 5, + "min_lag_sources": 3, + "max_lag_target": 7, + } nw_1 = MultivariateTE() data = Data(seed=SEED) data.generate_mute_data() sources = [1, 2, 3] target = 0 nw_1._initialise(settings, data, sources, target) - assert (nw_1._selected_vars_sources == - [i for i in it.product(sources, [nw_1.current_value[1]])]), ( - 'Did not add correct additional conditioning vars.') + assert nw_1._selected_vars_sources == [ + i for i in it.product(sources, [nw_1.current_value[1]]) + ], "Did not add correct additional conditioning vars." @jpype_missing def test_add_conditional_manually(): """Enforce the conditioning on additional variables.""" - settings = {'cmi_estimator': 'JidtKraskovCMI', - 'max_lag_sources': 5, - 'min_lag_sources': 3, - 'max_lag_target': 7} + settings = { + "cmi_estimator": "JidtKraskovCMI", + "max_lag_sources": 5, + "min_lag_sources": 3, + "max_lag_target": 7, + } nw = MultivariateTE() data = Data(seed=SEED) data.generate_mute_data() # Add a conditional with a lag bigger than the max_lag requested above - settings['add_conditionals'] = (8, 0) + settings["add_conditionals"] = (8, 0) with pytest.raises(IndexError): nw._initialise(settings, data, sources=[1, 2], target=0) # Add valid conditionals and test if they were added - settings['add_conditionals'] = [(0, 1), (1, 3)] + settings["add_conditionals"] = [(0, 1), (1, 3)] nw._initialise(settings=settings, data=data, target=0, sources=[1, 2]) # Get list of conditionals after intialisation and convert absolute samples # back to lags for comparison. cond_list = nw._idx_to_lag(nw.selected_vars_full) - assert settings['add_conditionals'][0] in cond_list, ( - 'First enforced conditional is missing from results.') - assert settings['add_conditionals'][1] in cond_list, ( - 'Second enforced conditional is missing from results.') + assert ( + settings["add_conditionals"][0] in cond_list + ), "First enforced conditional is missing from results." + assert ( + settings["add_conditionals"][1] in cond_list + ), "Second enforced conditional is missing from results." @jpype_missing @@ -328,31 +362,30 @@ def test_check_source_set(): data = Data(seed=SEED) data.generate_mute_data(100, 5) nw_0 = MultivariateTE() - nw_0.settings = {'verbose': True} + nw_0.settings = {"verbose": True} # Add list of sources. sources = [1, 2, 3] nw_0._check_source_set(sources, data.n_processes) - assert nw_0.source_set == sources, 'Sources were not added correctly.' + assert nw_0.source_set == sources, "Sources were not added correctly." # Assert that initialisation fails if the target is also in the source list sources = [0, 1, 2, 3] nw_0.target = 0 with pytest.raises(RuntimeError): - nw_0._check_source_set(sources=[0, 1, 2, 3], - n_processes=data.n_processes) + nw_0._check_source_set(sources=[0, 1, 2, 3], n_processes=data.n_processes) # Test if a single source, no list is added correctly. sources = 1 nw_0._check_source_set(sources, data.n_processes) - assert (type(nw_0.source_set) is list) + assert type(nw_0.source_set) is list # Test if 'all' is handled correctly nw_0.target = 0 - nw_0._check_source_set('all', data.n_processes) - assert nw_0.source_set == [1, 2, 3, 4], 'Sources were not added correctly.' + nw_0._check_source_set("all", data.n_processes) + assert nw_0.source_set == [1, 2, 3, 4], "Sources were not added correctly." # Test invalid inputs. - with pytest.raises(RuntimeError): # sources greater than no. procs + with pytest.raises(RuntimeError): # sources greater than no. procs nw_0._check_source_set(8, data.n_processes) with pytest.raises(RuntimeError): # negative value as source nw_0._check_source_set(-3, data.n_processes) @@ -366,32 +399,35 @@ def test_define_candidates(): max_lag_target = 10 current_val = (target, 10) procs = [target] - samples = np.arange(current_val[1] - 1, current_val[1] - max_lag_target, - -tau_target) + samples = np.arange( + current_val[1] - 1, current_val[1] - max_lag_target, -tau_target + ) # Test if candidates that are added manually to the conditioning set are # removed from the candidate set. nw = MultivariateTE() nw.current_value = current_val settings = [ - {'add_conditionals': None}, - {'add_conditionals': (2, 3)}, - {'add_conditionals': [(2, 3), (4, 1)]}, - {'add_conditionals': [(1, 9)]}, - {'add_conditionals': [(1, 9), (2, 3), (4, 1)]}] + {"add_conditionals": None}, + {"add_conditionals": (2, 3)}, + {"add_conditionals": [(2, 3), (4, 1)]}, + {"add_conditionals": [(1, 9)]}, + {"add_conditionals": [(1, 9), (2, 3), (4, 1)]}, + ] for s in settings: nw.settings = s candidates = nw._define_candidates(procs, samples) - assert (1, 9) in candidates, 'Sample missing from candidates: (1, 9).' - assert (1, 6) in candidates, 'Sample missing from candidates: (1, 6).' - assert (1, 3) in candidates, 'Sample missing from candidates: (1, 3).' - if s['add_conditionals'] is not None: - if type(s['add_conditionals']) is tuple: - cond_ind = nw._lag_to_idx([s['add_conditionals']]) + assert (1, 9) in candidates, "Sample missing from candidates: (1, 9)." + assert (1, 6) in candidates, "Sample missing from candidates: (1, 6)." + assert (1, 3) in candidates, "Sample missing from candidates: (1, 3)." + if s["add_conditionals"] is not None: + if isinstace(s["add_conditionals"], tuple): + cond_ind = nw._lag_to_idx([s["add_conditionals"]]) else: - cond_ind = nw._lag_to_idx(s['add_conditionals']) + cond_ind = nw._lag_to_idx(s["add_conditionals"]) for c in cond_ind: - assert c not in candidates, ( - 'Sample added erronously to candidates: {}.'.format(c)) + assert ( + c not in candidates + ), f"Sample added erronously to candidates: {c}." @jpype_missing @@ -401,109 +437,135 @@ def test_analyse_network(): data = Data(seed=SEED) data.generate_mute_data(10, 5) settings = { - 'cmi_estimator': 'JidtKraskovCMI', - 'n_perm_max_stat': 21, - 'n_perm_min_stat': 21, - 'n_perm_max_seq': 21, - 'n_perm_omnibus': 21, - 'max_lag_sources': 5, - 'min_lag_sources': 4, - 'max_lag_target': 5} + "cmi_estimator": "JidtKraskovCMI", + "n_perm_max_stat": 21, + "n_perm_min_stat": 21, + "n_perm_max_seq": 21, + "n_perm_omnibus": 21, + "max_lag_sources": 5, + "min_lag_sources": 4, + "max_lag_target": 5, + } nw_0 = MultivariateTE() # Test all to all analysis - results = nw_0.analyse_network( - settings, data, targets='all', sources='all') + results = nw_0.analyse_network(settings, data, targets="all", sources="all") targets_analysed = results.targets_analysed sources = np.arange(n_processes) - assert all(np.array(targets_analysed) == np.arange(n_processes)), ( - 'Network analysis did not run on all targets.') + assert all( + np.array(targets_analysed) == np.arange(n_processes) + ), "Network analysis did not run on all targets." for t in results.targets_analysed: s = np.array(list(set(sources) - set([t]))) - assert all(np.array(results._single_target[t].sources_tested) == s), ( - 'Network analysis did not run on all sources for target ' - '{0}'. format(t)) + assert all( + np.array(results._single_target[t].sources_tested) == s + ), f"Network analysis did not run on all sources for target {t}" # Test analysis for subset of targets target_list = [1, 2, 3] - results = nw_0.analyse_network( - settings, data, targets=target_list, sources='all') + results = nw_0.analyse_network(settings, data, targets=target_list, sources="all") targets_analysed = results.targets_analysed - assert all(np.array(targets_analysed) == np.array(target_list)), ( - 'Network analysis did not run on correct subset of targets.') + assert all( + np.array(targets_analysed) == np.array(target_list) + ), "Network analysis did not run on correct subset of targets." for t in results.targets_analysed: s = np.array(list(set(sources) - set([t]))) - assert all(np.array(results._single_target[t].sources_tested) == s), ( - 'Network analysis did not run on all sources for target ' - '{0}'. format(t)) + assert all( + np.array(results._single_target[t].sources_tested) == s + ), f"Network analysis did not run on all sources for target {t}" # Test analysis for subset of sources source_list = [1, 2, 3] target_list = [0, 4] - results = nw_0.analyse_network(settings, data, targets=target_list, - sources=source_list) + results = nw_0.analyse_network( + settings, data, targets=target_list, sources=source_list + ) targets_analysed = results.targets_analysed - assert all(np.array(targets_analysed) == np.array(target_list)), ( - 'Network analysis did not run for all targets.') + assert all( + np.array(targets_analysed) == np.array(target_list) + ), "Network analysis did not run for all targets." for t in results.targets_analysed: - assert all(results._single_target[t].sources_tested == - np.array(source_list)), ( - 'Network analysis did not run on the correct subset ' - 'of sources for target {0}'.format(t)) + assert all( + results._single_target[t].sources_tested == np.array(source_list) + ), f"Network analysis did not run on the correct subset of sources for target {t}" + + +def test_analyse_network_empty_target(): + # Test analyze network when one target has no selected sources such that + # for the Omnibus Test the defaults are not set. Ensure, combining results + # works if one target has no incoming links. + data = Data(seed=SEED) + data.generate_mute_data(104, 10) + np.random.seed(SEED) + data._data[0, :, :] = np.random.rand(104, 10) + settings = { + "cmi_estimator": "JidtKraskovCMI", + "n_perm_max_stat": 21, + "n_perm_min_stat": 21, + "n_perm_max_seq": 21, + # "n_perm_omnibus": 21, + "max_lag_sources": 5, + "min_lag_sources": 4, + "max_lag_target": 5, + } + # For the first target, no sources are selected and hence Omnibus defaults + # are not set. If later a target has inputs, the settings of the combined + # results structure will contain the Omnibus settings from that target's + # analysis. + nw = MultivariateTE() + nw.analyse_network(settings, data, targets=[4, 2], sources=[[0], [0, 1, 3]]) @jpype_missing def test_permute_time(): """Create surrogates by permuting data in time instead of over replic.""" # Test if perm type is set to default - default = 'random' + default = "random" data = Data(seed=SEED) data.generate_mute_data(10, 5) settings = { - 'cmi_estimator': 'JidtKraskovCMI', - 'n_perm_max_stat': 21, - 'n_perm_max_seq': 21, - 'n_perm_omnibus': 21, - 'max_lag_sources': 5, - 'min_lag_sources': 4, - 'max_lag_target': 5, - 'permute_in_time': True} + "cmi_estimator": "JidtKraskovCMI", + "n_perm_max_stat": 21, + "n_perm_max_seq": 21, + "n_perm_omnibus": 21, + "max_lag_sources": 5, + "min_lag_sources": 4, + "max_lag_target": 5, + "permute_in_time": True, + } nw_0 = MultivariateTE() - results = nw_0.analyse_network( - settings, data, targets='all', sources='all') - assert results.settings.perm_type == default, ( - 'Perm type was not set to default.') + results = nw_0.analyse_network(settings, data, targets="all", sources="all") + assert results.settings.perm_type == default, "Perm type was not set to default." def test_discrete_input(): """Test multivariate TE estimation from discrete data.""" # Generate Gaussian test data covariance = 0.4 - data = _get_discrete_gauss_data(covariance=covariance, - n=10000, - delay=1, - normalise=False, - seed=SEED) - corr_expected = covariance / ( - 1 * np.sqrt(covariance**2 + (1-covariance)**2)) + data = _get_discrete_gauss_data( + covariance=covariance, n=10000, delay=1, normalise=False, seed=SEED + ) + corr_expected = covariance / (1 * np.sqrt(covariance**2 + (1 - covariance) ** 2)) expected_mi = calculate_mi(corr_expected) settings = { - 'cmi_estimator': 'JidtDiscreteCMI', - 'discretise_method': 'none', - 'n_discrete_bins': 5, # alphabet size of the variables analysed - 'n_perm_max_stat': 21, - 'n_perm_omnibus': 30, - 'n_perm_max_seq': 30, - 'min_lag_sources': 1, - 'max_lag_sources': 2, - 'max_lag_target': 1} + "cmi_estimator": "JidtDiscreteCMI", + "discretise_method": "none", + "n_discrete_bins": 5, # alphabet size of the variables analysed + "n_perm_max_stat": 21, + "n_perm_omnibus": 30, + "n_perm_max_seq": 30, + "min_lag_sources": 1, + "max_lag_sources": 2, + "max_lag_target": 1, + } nw = MultivariateTE() res = nw.analyse_single_target(settings=settings, data=data, target=1) - assert np.isclose( - res._single_target[1].omnibus_te, expected_mi, atol=0.05), ( - 'Estimated TE for discrete variables is not correct. Expected: ' - '{0}, Actual results: {1}.'.format( - expected_mi, res._single_target[1].omnibus_te)) + assert np.isclose(res._single_target[1].omnibus_te, expected_mi, atol=0.05), ( + "Estimated TE for discrete variables is not correct. Expected: " + "{0}, Actual results: {1}.".format( + expected_mi, res._single_target[1].omnibus_te + ) + ) def test_include_target_candidates(): @@ -535,64 +597,67 @@ def test_checkpoint(): n_processes = 5 # the MuTE network has 5 nodes data = Data(seed=SEED) data.generate_mute_data(10, 5) - filename_ckp = './my_checkpoint' + filename_ckp = "./my_checkpoint" settings = { - 'cmi_estimator': 'JidtKraskovCMI', - 'n_perm_max_stat': 21, - 'n_perm_min_stat': 21, - 'n_perm_max_seq': 21, - 'n_perm_omnibus': 21, - 'max_lag_sources': 5, - 'min_lag_sources': 4, - 'max_lag_target': 5, - 'write_ckp': True, - 'filename_ckp': filename_ckp} + "cmi_estimator": "JidtKraskovCMI", + "n_perm_max_stat": 21, + "n_perm_min_stat": 21, + "n_perm_max_seq": 21, + "n_perm_omnibus": 21, + "max_lag_sources": 5, + "min_lag_sources": 4, + "max_lag_target": 5, + "write_ckp": True, + "filename_ckp": filename_ckp, + } nw_0 = MultivariateTE() # Test all to all analysis - results = nw_0.analyse_network( - settings, data, targets='all', sources='all') + results = nw_0.analyse_network(settings, data, targets="all", sources="all") targets_analysed = results.targets_analysed sources = np.arange(n_processes) - assert all(np.array(targets_analysed) == np.arange(n_processes)), ( - 'Network analysis did not run on all targets.') + assert all( + np.array(targets_analysed) == np.arange(n_processes) + ), "Network analysis did not run on all targets." for t in results.targets_analysed: s = np.array(list(set(sources) - set([t]))) - assert all(np.array(results._single_target[t].sources_tested) == s), ( - 'Network analysis did not run on all sources for target ' - '{0}'. format(t)) + assert all( + np.array(results._single_target[t].sources_tested) == s + ), "Network analysis did not run on all sources for target " "{0}".format(t) # Test analysis for subset of targets target_list = [1, 2, 3] - results = nw_0.analyse_network( - settings, data, targets=target_list, sources='all') + results = nw_0.analyse_network(settings, data, targets=target_list, sources="all") targets_analysed = results.targets_analysed - assert all(np.array(targets_analysed) == np.array(target_list)), ( - 'Network analysis did not run on correct subset of targets.') + assert all( + np.array(targets_analysed) == np.array(target_list) + ), "Network analysis did not run on correct subset of targets." for t in results.targets_analysed: s = np.array(list(set(sources) - set([t]))) - assert all(np.array(results._single_target[t].sources_tested) == s), ( - 'Network analysis did not run on all sources for target ' - '{0}'. format(t)) + assert all( + np.array(results._single_target[t].sources_tested) == s + ), "Network analysis did not run on all sources for target " "{0}".format(t) # Test analysis for subset of sources source_list = [1, 2, 3] target_list = [0, 4] - results = nw_0.analyse_network(settings, data, targets=target_list, - sources=source_list) + results = nw_0.analyse_network( + settings, data, targets=target_list, sources=source_list + ) targets_analysed = results.targets_analysed - assert all(np.array(targets_analysed) == np.array(target_list)), ( - 'Network analysis did not run for all targets.') + assert all( + np.array(targets_analysed) == np.array(target_list) + ), "Network analysis did not run for all targets." for t in results.targets_analysed: - assert all(results._single_target[t].sources_tested == - np.array(source_list)), ( - 'Network analysis did not run on the correct subset ' - 'of sources for target {0}'.format(t)) + assert all( + results._single_target[t].sources_tested == np.array(source_list) + ), f"Network analysis did not run on the correct subset of sources for target {t}" _clear_ckp(filename_ckp) -if __name__ == '__main__': +if __name__ == "__main__": + test_analyse_network_empty_target() test_return_local_values() test_discrete_input() test_analyse_network() From 4fc866d0cf165ba538cb5958e36711496792a37c Mon Sep 17 00:00:00 2001 From: Patricia Wollstadt Date: Mon, 29 Jan 2024 17:06:13 +0100 Subject: [PATCH 23/34] Fix formatting and pylint errors --- idtxl/results.py | 40 +++++++++++++++++----------------------- 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/idtxl/results.py b/idtxl/results.py index ce33470..b4ffacf 100644 --- a/idtxl/results.py +++ b/idtxl/results.py @@ -171,16 +171,13 @@ def _check_result(self, process, settings): # Check if new result process is part of the network if process > (self.data_properties.n_nodes - 1): raise RuntimeError( - "Can not add single result - process {0} is not" - " in no. nodes in the data ({1}).".format( - process, self.data_properties.n_nodes - ) + f"Can not add single result - process {process} is not" + f" in no. nodes in the data ({self.data_properties.n_nodes})." ) # Don't add duplicate processes if self._is_duplicate_process(process): raise RuntimeError( - "Can not add single result - results for target" - " or process {0} already exist.".format(process) + f"Can not add single result - results for target or process {process} already exist." ) # Don't add results with conflicting settings if utils.conflicting_entries(self.settings, settings): @@ -192,8 +189,7 @@ def _is_duplicate_process(self, process): # Test if process is already present in object if process in self._processes_analysed: return True - else: - return False + return False def combine_results(self, *results): """Combine multiple (partial) results objects. @@ -226,15 +222,14 @@ def combine_results(self, *results): processes = r._processes_analysed if utils.conflicting_entries(self.settings, r.settings): raise RuntimeError( - "Can not combine results - analysis " "settings are not equal." + "Can not combine results - analysis settings are not equal." ) for p in processes: # Remove potential partial FDR-corrected results. These are no # longer valid for the combined network. if self._is_duplicate_process(p): raise RuntimeError( - "Can not combine results - results for " - "process {0} already exist.".format(p) + f"Can not combine results - results for process {p} already exist." ) try: del r.fdr_corrected @@ -247,11 +242,10 @@ def combine_results(self, *results): except AttributeError: try: results_to_add = r._single_process[p] - except AttributeError: + except AttributeError as e: raise AttributeError( - "Did not find any method attributes to combine " - "(.single_proces or ._single_target)." - ) + "Did not find any method attributes to combine (.single_process or ._single_target)." + ) from e self._add_single_result(p, results_to_add, r.settings) @@ -366,21 +360,21 @@ def get_single_process(self, process, fdr=True): if fdr: try: return self._single_process_fdr[process] - except AttributeError: + except AttributeError as e: raise RuntimeError( f"No FDR-corrected results for process {process}. Set fdr=False for uncorrected results." - ) - except KeyError: + ) from e + except KeyError as e: raise RuntimeError( f"No FDR-corrected results for process {process}. Set fdr=False for uncorrected results." - ) + ) from e else: try: return self._single_process[process] - except AttributeError: - raise RuntimeError("No results have been added.") - except KeyError: - raise RuntimeError("No results for process {0}.".format(process)) + except AttributeError as e: + raise RuntimeError("No results have been added.") from e + except KeyError as e: + raise RuntimeError(f"No results for process {process}.") from e def get_significant_processes(self, fdr=True): """Return statistically-significant processes. From ac1ecee959ac5d2eba825bec4303281c539dd899 Mon Sep 17 00:00:00 2001 From: Patricia Wollstadt Date: Mon, 29 Jan 2024 17:19:43 +0100 Subject: [PATCH 24/34] Fix typo --- test/test_multivariate_te.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_multivariate_te.py b/test/test_multivariate_te.py index e18c325..fafeb8e 100644 --- a/test/test_multivariate_te.py +++ b/test/test_multivariate_te.py @@ -420,7 +420,7 @@ def test_define_candidates(): assert (1, 6) in candidates, "Sample missing from candidates: (1, 6)." assert (1, 3) in candidates, "Sample missing from candidates: (1, 3)." if s["add_conditionals"] is not None: - if isinstace(s["add_conditionals"], tuple): + if isinstance(s["add_conditionals"], tuple): cond_ind = nw._lag_to_idx([s["add_conditionals"]]) else: cond_ind = nw._lag_to_idx(s["add_conditionals"]) From 44218dd8cde5c461bd594d1cb7787874c11e7d91 Mon Sep 17 00:00:00 2001 From: Patricia Wollstadt Date: Fri, 2 Feb 2024 12:10:08 +0100 Subject: [PATCH 25/34] Refactor bivariate sequential max stats --- idtxl/stats.py | 76 ++++++++++++++++++++++++-------------------------- 1 file changed, 37 insertions(+), 39 deletions(-) diff --git a/idtxl/stats.py b/idtxl/stats.py index de7e84f..44407ca 100644 --- a/idtxl/stats.py +++ b/idtxl/stats.py @@ -196,7 +196,9 @@ def network_fdr(settings=None, *results): cands = cands + (results_comb._single_target[target].selected_vars_sources) n_perm = np.append(n_perm, results_comb.settings.n_perm_max_seq) - if pval.size == 0 or (n_perm == None).all(): # n_perm is None for empty networks, i.e., no omnibus test was run + if ( + pval.size == 0 or (n_perm == None).all() + ): # n_perm is None for empty networks, i.e., no omnibus test was run print("No links in final results ...") results_comb._add_fdr( fdr=None, @@ -752,14 +754,13 @@ def max_statistic_sequential_bivariate(analysis_setup, data): alpha = analysis_setup.settings["alpha_max_seq"] _check_permute_in_time(analysis_setup, data, n_permutations) permute_in_time = analysis_setup.settings["permute_in_time"] + assert analysis_setup.selected_vars_sources, "No sources to test." if analysis_setup.settings["verbose"]: print( f"sequential maximum statistic, n_perm: {n_permutations}, testing {len(analysis_setup.selected_vars_sources)} selected sources" ) - assert analysis_setup.selected_vars_sources, "No sources to test." - # Check if target variables were selected to distinguish between TE and MI # analysis. if len(analysis_setup._selected_vars_target) == 0: @@ -783,20 +784,18 @@ def max_statistic_sequential_bivariate(analysis_setup, data): source_vars = [ s for s in analysis_setup.selected_vars_sources if s[0] == source ] - source_vars_idx = [ - i - for i, s in enumerate(analysis_setup.selected_vars_sources) - if s[0] == source - ] # Determine length of conditioning set and allocate memory. - idx_conditional = source_vars.copy() if conditional_realisations_target is not None: - idx_conditional += analysis_setup.selected_vars_target + size_conditioning_set = len(source_vars) + len( + analysis_setup.selected_vars_target + ) + else: + size_conditioning_set = len(source_vars) conditional_realisations = np.empty( ( data.n_realisations(analysis_setup.current_value) * len(source_vars), - len(idx_conditional) - 1, + size_conditioning_set - 1, ) ).astype(data.data_type) candidate_realisations = np.empty( @@ -812,28 +811,33 @@ def max_statistic_sequential_bivariate(analysis_setup, data): surr_table = np.zeros((len(source_vars), n_permutations)) # Collect data for each candidate and the corresponding conditioning set. for idx_c, candidate in enumerate(source_vars): - temp_cond = data.get_realisations( + realizations_remaining_source_vars = data.get_realisations( analysis_setup.current_value, set(source_vars).difference(set([candidate])), )[0] - temp_cand = data.get_realisations( + realizations_current_candidate = data.get_realisations( analysis_setup.current_value, [candidate] )[0] # The following may happen if either the requested conditioning is # 'none' or if the conditioning set that is tested consists only of # a single candidate. - if temp_cond is None: + if realizations_remaining_source_vars is None: conditional_realisations = conditional_realisations_target re_use = ["var2", "conditional"] else: re_use = ["var2"] if conditional_realisations_target is None: - conditional_realisations[i_1:i_2,] = temp_cond + conditional_realisations[ + i_1:i_2, + ] = realizations_remaining_source_vars else: conditional_realisations[i_1:i_2,] = np.hstack( - (temp_cond, conditional_realisations_target) + ( + realizations_remaining_source_vars, + conditional_realisations_target, + ) ) - candidate_realisations[i_1:i_2,] = temp_cand + candidate_realisations[i_1:i_2,] = realizations_current_candidate i_1 = i_2 i_2 += data.n_realisations(analysis_setup.current_value) @@ -851,7 +855,7 @@ def max_statistic_sequential_bivariate(analysis_setup, data): analysis_setup.current_value, [candidate] )[0], var2=analysis_setup._current_value_realisations, - conditional=temp_cond, + conditional=realizations_remaining_source_vars, ) else: analysis_setup.settings["analytical_surrogates"] = False @@ -870,7 +874,7 @@ def max_statistic_sequential_bivariate(analysis_setup, data): re_use=["var2", "conditional"], var1=surr_candidate_realisations, var2=analysis_setup._current_value_realisations, - conditional=temp_cond, + conditional=realizations_remaining_source_vars, ) except ex.AlgorithmExhaustedError as aee: # The aglorithm cannot continue here, so @@ -898,7 +902,7 @@ def max_statistic_sequential_bivariate(analysis_setup, data): conditional=conditional_realisations, ) except ex.AlgorithmExhaustedError as aee: - # The aglorithm cannot continue here, so + # The algorithm cannot continue here, so # we'll terminate the max sequential stats test, # and declare all not significant print( @@ -907,21 +911,18 @@ def max_statistic_sequential_bivariate(analysis_setup, data): ) # For now we don't need a stack trace: # traceback.print_tb(aee.__traceback__) - # Return (signficance, pvalue, TEs): + # Return (significance, pvalue, TEs): return ( np.zeros(len(analysis_setup.selected_vars_sources)).astype(bool), np.ones(len(analysis_setup.selected_vars_sources)), np.zeros(len(analysis_setup.selected_vars_sources)), ) - selected_vars_order = utils.argsort_descending(individual_stat_source) - individual_stat_source_sorted = utils.sort_descending(individual_stat_source) - max_distribution = _sort_table_max(surr_table) - significance_source = np.zeros(individual_stat_source.shape[0], dtype=bool) - pvalue_source = np.ones(individual_stat_source.shape[0]) - # Compare each original value with the distribution of the same rank, # starting with the highest value. + individual_stat_source_sorted = utils.sort_descending(individual_stat_source) + source_vars_sorted = [source_vars[i] for i in utils.argsort_descending(individual_stat_source)] + max_distribution = _sort_table_max(surr_table) for c in range(individual_stat_source.shape[0]): s, p = _find_pvalue( individual_stat_source_sorted[c], @@ -929,24 +930,21 @@ def max_statistic_sequential_bivariate(analysis_setup, data): alpha, tail="one_bigger", ) - significance_source[c] = s - pvalue_source[c] = p - if not s: # break as soon as a candidate is no longer significant if analysis_setup.settings["verbose"]: print( f"\nStopping sequential max stats at candidate with rank {c}." ) break + # Ensure results are written in the original order of selected + # source variables + source_var_idx = analysis_setup.selected_vars_sources.index( + source_vars_sorted[c] + ) + significance[source_var_idx] = s + pvalue[source_var_idx] = p + individual_stat[source_var_idx] = individual_stat_source_sorted[c] - # Get back original order of variables within the current source. Write - # re-ordered results into global results array at the respective - # variable locations. - significance_source[selected_vars_order] = significance_source.copy() - pvalue_source[selected_vars_order] = pvalue_source.copy() - significance[source_vars_idx] = significance_source - pvalue[source_vars_idx] = pvalue_source - individual_stat[source_vars_idx] = individual_stat_source return significance, pvalue, individual_stat @@ -1462,7 +1460,7 @@ def _sort_table_min(table): def _sort_table_max(table): """Sort each column in a table in descending order.""" table_sorted = np.empty(table.shape) - for permutation in range(0, table.shape[1]): + for permutation in range(table.shape[1]): table_sorted[:, permutation] = utils.sort_descending(table[:, permutation]) return table_sorted From 208dde198ae495ee7ce7f3ed0042146284e5fee3 Mon Sep 17 00:00:00 2001 From: Patricia Wollstadt Date: Fri, 2 Feb 2024 14:30:58 +0100 Subject: [PATCH 26/34] Add unit tests for (bivariate) sequential max stats --- test/test_stats.py | 105 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 104 insertions(+), 1 deletion(-) diff --git a/test/test_stats.py b/test/test_stats.py index 872cd65..7d03a15 100644 --- a/test/test_stats.py +++ b/test/test_stats.py @@ -1,5 +1,7 @@ """Unit tests for stats module.""" # pylint: disable=protected-access +import copy as cp + import numpy as np import pytest from test_estimators_jidt import _get_gauss_data @@ -49,7 +51,14 @@ def test_max_statistic_sequential(): setup.current_value, [setup.current_value] )[0] - setup._selected_vars_realisations = np.hstack( + setup._selected_vars_realisations = data.get_realisations( # use actual realisation for target var and first source var + setup.current_value, + [setup.selected_vars_target[0]] + setup.selected_vars_sources, + )[ + 0 + ] + setup_permuted = cp.deepcopy(setup) + setup_permuted._selected_vars_realisations = np.hstack( ( data.get_realisations( # use actual realisation for target var and first source var setup.current_value, @@ -62,7 +71,100 @@ def test_max_statistic_sequential(): ), # use random data as realizations for second source var ) ) + for s, expected in zip([setup, setup_permuted], [[True, True], [True, False]]): + [sign, p, te] = stats.max_statistic_sequential(analysis_setup=s, data=data) + print(p) + print(sign) + print(te) + assert len(p) == len(te) + assert len(p) == len(sign) + assert len(p) == len(setup.selected_vars_sources) + assert np.array_equal(sign, expected), "Incorrect sources inferred" + + setup = MultivariateTE() + setup._initialise(settings, data, sources=[0, 1, 4], target=3) + setup.current_value = (3, 4) + setup.selected_vars_target = [(1, 1)] + setup.selected_vars_sources = [(0, 1), (0, 2), (1, 1), (4, 1)] + setup.selected_vars_full = setup.selected_vars_target + setup.selected_vars_sources + setup._current_value_realisations = data.get_realisations( + setup.current_value, [setup.current_value] + )[0] + setup._selected_vars_realisations = data.get_realisations( + setup.current_value, setup.selected_vars_full + )[0] + [sign, p, te] = stats.max_statistic_sequential(analysis_setup=setup, data=data) + assert len(p) == len(te) + assert len(p) == len(sign) + assert len(p) == len(setup.selected_vars_sources) + print(p) + print(setup.selected_vars_sources) + print(sign) + print(te, "\n\n") + + for permuted_var in [0, 4]: + np.random.seed(0) + data.generate_mute_data(104, 10) + data._data[permuted_var, :, :] = np.ones((104, 10)) + setup._current_value_realisations = data.get_realisations( + setup.current_value, [setup.current_value] + )[0] + setup._selected_vars_realisations = data.get_realisations( + setup.current_value, setup.selected_vars_full + )[0] + [sign, p, te] = stats.max_statistic_sequential(analysis_setup=setup, data=data) + for i, var in enumerate(setup.selected_vars_sources): + if var[0] == permuted_var: + assert not sign[ + i + ], "Seq. max. stats returned sign result for random data" + assert ( + p[i] > setup.settings["alpha_max_seq"] + ), f"{p[i]} not smaller than critical alpha {setup.settings['alpha_max_seq']}" + + +def test_max_statistic_sequential_bivariate(): + np.random.seed(0) + data = Data() + data.generate_mute_data(104, 10) + settings = { + "cmi_estimator": "JidtKraskovCMI", + "n_perm_max_stat": 21, + "n_perm_min_stat": 21, + "n_perm_omnibus": 21, + "n_perm_max_seq": 21, + "max_lag_sources": 5, + "min_lag_sources": 1, + "max_lag_target": 5, + } + setup = MultivariateTE() + setup._initialise(settings, data, sources=[0, 2], target=1) + setup.current_value = (1, 4) + setup.selected_vars_target = [(1, 1)] + setup.selected_vars_sources = [(0, 1), (2, 1)] + setup.selected_vars_full = setup.selected_vars_target + setup.selected_vars_sources + + setup._current_value_realisations = data.get_realisations( + setup.current_value, [setup.current_value] + )[0] + + setup._selected_vars_realisations = np.hstack( + ( + data.get_realisations( # use actual realisation for target var and first source var + setup.current_value, + [setup.selected_vars_target[0], setup.selected_vars_sources[0]], + )[ + 0 + ], + np.random.rand( + data.n_realisations(setup.current_value), 1 + ), # use random data as realizations for second source var + ) + ) + [sign, p, te] = stats.max_statistic_sequential_bivariate( + analysis_setup=setup, data=data + ) print(p) print(sign) print(te) @@ -508,4 +610,5 @@ def test_analytical_surrogates(): # test_omnibus_test() # test_max_statistic() # test_min_statistic() + test_max_statistic_sequential_bivariate() test_max_statistic_sequential() From 47773cffb8fd8c47608cb32447bf1617163bfba9 Mon Sep 17 00:00:00 2001 From: "David A. Ehrlich" Date: Fri, 2 Feb 2024 14:00:06 +0100 Subject: [PATCH 27/34] Fix bug in _perform_fdr_correction by replacing with statsmodels --- idtxl/stats.py | 61 +++++++++++++++++++++----------------------------- 1 file changed, 25 insertions(+), 36 deletions(-) diff --git a/idtxl/stats.py b/idtxl/stats.py index 44407ca..8f6b8ce 100644 --- a/idtxl/stats.py +++ b/idtxl/stats.py @@ -3,6 +3,7 @@ import copy as cp import numpy as np +from statsmodels.stats.multitest import fdrcorrection from . import idtxl_exceptions as ex from . import idtxl_utils as utils @@ -72,16 +73,16 @@ def ais_fdr(settings=None, *results): results_comb._add_fdr(fdr=None, alpha=alpha, constant=constant) return results_comb - sign, thresh = _perform_fdr_corretion( + sign, min_thresh = _perform_fdr_correction( pval, constant, alpha, len(results_comb.processes_analysed) ) # If the number of permutations for calculating p-values for individual # variables is too low, return without performing any correction. - if (1 / min(n_perm)) > thresh[0]: + if (1 / min(n_perm)) > min_thresh: print( "WARNING: Number of permutations ('n_perm_max_seq') for at least one target is too low to allow for " - f"FDR correction (FDR-threshold: {thresh[0]:.4f}, min. theoretically possible p-value: {1 / min(n_perm)})." + f"FDR correction (FDR-threshold: {min_thresh:.4f}, min. theoretically possible p-value: {1 / min(n_perm)})." ) results_comb._add_fdr(fdr=None, alpha=alpha, constant=constant) return results_comb @@ -208,14 +209,14 @@ def network_fdr(settings=None, *results): ) return results_comb - sign, thresh = _perform_fdr_corretion(pval, constant, alpha, n_tests) + sign, min_thresh = _perform_fdr_correction(pval, constant, alpha, n_tests) # If the number of permutations for calculating p-values for individual # variables is too low, return without performing any correction. - if (1 / min(i for i in n_perm if i is not None)) > thresh[0]: + if (1 / min(i for i in n_perm if i is not None)) > min_thresh: print( "WARNING: Number of permutations ('n_perm_max_seq') for at least one target is too low to allow for " - f"FDR correction (FDR-threshold: {thresh[0]:.4f}, min. theoretically possible p-value: {1 / min(n_perm)})." + f"FDR correction (FDR-threshold: {min_thresh:.4f}, min. theoretically possible p-value: {1 / min(n_perm)})." ) results_comb._add_fdr( fdr=None, @@ -254,13 +255,16 @@ def network_fdr(settings=None, *results): return results_comb -def _perform_fdr_corretion(pval, constant, alpha, n_tests): +def _perform_fdr_correction(pval, constant, alpha, n_tests): """Calculate sequential threshold for FDR-correction. Calculate sequential thresholds for FDR-correction of p-values. The constant defines how the threshold is calculated. See Genovese (2002) for details. + Internally uses the statsmodels implementation of the Benjamini-Hochberg + and Benjamini-Yekutieli procedures for FDR-correction. + References: - Genovese, C.R., Lazar, N.A., & Nichols, T. (2002). Thresholding of @@ -283,36 +287,21 @@ def _perform_fdr_corretion(pval, constant, alpha, n_tests): Returns: array of bools significance of p-values in the order of the input array - array of floats - FDR-thresholds for each p-value in increasing order + float + smallest threshold for significance """ - # Sort all p-values in ascending order. - sort_idx = np.argsort(pval) - pval_sorted = np.sort(pval) - - # Calculate threshold - if constant == 2: # pick the requested constant (see Genovese, p.872) - if n_tests < 1000: - const = np.sum(1 / np.arange(1, n_tests + 1)) - else: - const = np.log(n_tests) + np.e # aprx. harmonic sum with Euler's number - elif constant == 1: - # This is less strict than the other one and corresponds to a - # Bonoferroni-correction for the first p-value, however, it makes more - # strict assumptions on the distribution of p-values, while constant 2 - # works for any joint distribution of the p-values. - const = 1 - - # Calculate threshold for each p-value. - thresh = (np.arange(1, len(pval_sorted) + 1) / n_tests) * alpha / const - - # Compare data to threshold. - sign = pval_sorted <= thresh - if np.invert(sign).any(): - first_false = np.where(np.invert(sign))[0][0] - sign[first_false:] = False # avoids false positives due to equal pvals - sign[sort_idx] = sign.copy() # restore original ordering of significance values - return sign, thresh + + # Convert constant to statsmodels "method" parameter + method = "indep" if constant == 1 else "negcorr" + sign, _ = fdrcorrection(pval, alpha=alpha, method=method) + + # Compute smallest threshold to check for sufficiency of permutations + if constant == 1: + min_thresh = alpha / n_tests + else: + min_thresh = alpha / (n_tests * np.sum(1 / np.arange(1, n_tests + 1))) + + return sign, min_thresh def omnibus_test(analysis_setup, data): From 4f9ab51344739ce1b202186cc24db1730778d120 Mon Sep 17 00:00:00 2001 From: Patricia Wollstadt Date: Fri, 2 Feb 2024 14:48:07 +0100 Subject: [PATCH 28/34] Adapt unit tests to fixed FDR correction --- idtxl/stats.py | 6 ++++-- test/test_stats.py | 12 ++++++------ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/idtxl/stats.py b/idtxl/stats.py index 8f6b8ce..fcf8a19 100644 --- a/idtxl/stats.py +++ b/idtxl/stats.py @@ -290,7 +290,7 @@ def _perform_fdr_correction(pval, constant, alpha, n_tests): float smallest threshold for significance """ - + # Convert constant to statsmodels "method" parameter method = "indep" if constant == 1 else "negcorr" sign, _ = fdrcorrection(pval, alpha=alpha, method=method) @@ -910,7 +910,9 @@ def max_statistic_sequential_bivariate(analysis_setup, data): # Compare each original value with the distribution of the same rank, # starting with the highest value. individual_stat_source_sorted = utils.sort_descending(individual_stat_source) - source_vars_sorted = [source_vars[i] for i in utils.argsort_descending(individual_stat_source)] + source_vars_sorted = [ + source_vars[i] for i in utils.argsort_descending(individual_stat_source) + ] max_distribution = _sort_table_max(surr_table) for c in range(individual_stat_source.shape[0]): s, p = _find_pvalue( diff --git a/test/test_stats.py b/test/test_stats.py index 7d03a15..16460c7 100644 --- a/test/test_stats.py +++ b/test/test_stats.py @@ -242,7 +242,7 @@ def test_network_fdr(): "sources_tested": [0, 2], "omnibus_pval": 0.031, "omnibus_sign": True, - "selected_sources_pval": np.array([0.00001, 0.00014, 0.01]), + "selected_sources_pval": np.array([0.00001, 0.00014, 0.049]), "selected_sources_te": np.array([1.8, 1.75, 0.75]), } target_1["selected_vars_full"] = ( @@ -366,12 +366,12 @@ def test_fdr_sorting(): pval_sorted = np.arange(n_tests) / 300 constant = 2 alpha = 0.05 - sign, _ = stats._perform_fdr_corretion(pval_sorted, constant, alpha, n_tests) + sign, _ = stats._perform_fdr_correction(pval_sorted, constant, alpha, n_tests) assert sign[:3].all() permutation = np.random.permutation(n_tests) pval_unsorted = pval_sorted[permutation].copy() - sign_unsorted, _ = stats._perform_fdr_corretion( + sign_unsorted, _ = stats._perform_fdr_correction( pval_unsorted, constant, alpha, n_tests ) assert sign_unsorted.sum() == 3 @@ -600,7 +600,7 @@ def test_analytical_surrogates(): # test_ais_fdr() # test_analytical_surrogates() # test_data_type() - # test_network_fdr() + test_network_fdr() # test_fdr_sorting() # test_find_pvalue() # test_find_table_max() @@ -610,5 +610,5 @@ def test_analytical_surrogates(): # test_omnibus_test() # test_max_statistic() # test_min_statistic() - test_max_statistic_sequential_bivariate() - test_max_statistic_sequential() + # test_max_statistic_sequential_bivariate() + # test_max_statistic_sequential() From 8461992a968598ae7c95000207e844717a3da6c9 Mon Sep 17 00:00:00 2001 From: Patricia Wollstadt Date: Fri, 2 Feb 2024 15:37:42 +0100 Subject: [PATCH 29/34] Fix output of TE/MI values if biv. seq. max stats exits early If a source was not significant, the computation of p-values was stopped and also the TE/MI values were not written into the output array further. Ensure that always all TE/MI values are returned, also the non-significant ones. --- idtxl/stats.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/idtxl/stats.py b/idtxl/stats.py index fcf8a19..e65215d 100644 --- a/idtxl/stats.py +++ b/idtxl/stats.py @@ -907,6 +907,11 @@ def max_statistic_sequential_bivariate(analysis_setup, data): np.zeros(len(analysis_setup.selected_vars_sources)), ) + # Assign estimated TE/MI values to output structure. These are returned + # regardless of whether they are significant. + for s, stat in zip(source_vars, individual_stat_source): + individual_stat[analysis_setup.selected_vars_sources.index(s)] = stat + # Compare each original value with the distribution of the same rank, # starting with the highest value. individual_stat_source_sorted = utils.sort_descending(individual_stat_source) @@ -934,7 +939,6 @@ def max_statistic_sequential_bivariate(analysis_setup, data): ) significance[source_var_idx] = s pvalue[source_var_idx] = p - individual_stat[source_var_idx] = individual_stat_source_sorted[c] return significance, pvalue, individual_stat From ec6ee52e752921f7a62f5244e83f913e03d5d774 Mon Sep 17 00:00:00 2001 From: Patricia Wollstadt Date: Mon, 5 Feb 2024 15:13:10 +0100 Subject: [PATCH 30/34] Fix typos in comments and log output --- idtxl/bivariate_te.py | 6 +++--- idtxl/stats.py | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/idtxl/bivariate_te.py b/idtxl/bivariate_te.py index b380b6e..18a4e5b 100644 --- a/idtxl/bivariate_te.py +++ b/idtxl/bivariate_te.py @@ -7,9 +7,9 @@ Note: Written for Python 3.4+ """ -from .network_inference import NetworkInferenceTE, NetworkInferenceBivariate -from .stats import network_fdr +from .network_inference import NetworkInferenceBivariate, NetworkInferenceTE from .results import ResultsNetworkInference +from .stats import network_fdr class BivariateTE(NetworkInferenceTE, NetworkInferenceBivariate): @@ -157,7 +157,7 @@ def analyse_network(self, settings, data, targets="all", sources="all"): ) for t, target in enumerate(targets): if settings["verbose"]: - print("\n####### analysing target with index {t} from list {targets}") + print(f"\n####### analysing target with index {t} from list {targets}") res_single = self.analyse_single_target(settings, data, target, sources[t]) results.combine_results(res_single) diff --git a/idtxl/stats.py b/idtxl/stats.py index e65215d..f59a0e8 100644 --- a/idtxl/stats.py +++ b/idtxl/stats.py @@ -575,9 +575,9 @@ def max_statistic_sequential(analysis_setup, data): candidate_realisations_current, ] = analysis_setup._separate_realisations(idx_conditional, candidate) - # The following may happen if either the requested conditing is 'none' - # or if the conditiong set that is tested consists only of a single - # candidate. + # The following may happen if either the requested conditioning is + # 'none' or if the conditioning set that is tested consists only of a + # single candidate. if conditional_realisations_current is None: conditional_realisations = None re_use = ["var2", "conditional"] @@ -1496,7 +1496,7 @@ def _find_pvalue(statistic, distribution, alpha, tail): alpha = alpha / 2 else: raise ValueError( - f"Unkown value for tail: {tail}, should be one, one_bigger, one_smaller, or two" + f"Unknown value for tail: {tail}, should be one, one_bigger, one_smaller, or two" ) # If the statistic is larger than all values in the test distribution, set From dd8d0355134deeb6c10f8379777ab6fa9887146b Mon Sep 17 00:00:00 2001 From: Patricia Wollstadt Date: Mon, 5 Feb 2024 15:13:54 +0100 Subject: [PATCH 31/34] Add unit tests for (bivariate) seq. max stats --- test/test_stats.py | 105 +++++++++++++++++++++++++-------------------- 1 file changed, 59 insertions(+), 46 deletions(-) diff --git a/test/test_stats.py b/test/test_stats.py index 16460c7..0f56abd 100644 --- a/test/test_stats.py +++ b/test/test_stats.py @@ -13,6 +13,8 @@ from idtxl.multivariate_te import MultivariateTE from idtxl.results import ResultsNetworkInference, ResultsSingleProcessAnalysis +SEED = 0 + def test_omnibus_test(): print("Write test for omnibus test.") @@ -27,9 +29,9 @@ def test_min_statistic(): def test_max_statistic_sequential(): - np.random.seed(0) - data = Data() - data.generate_mute_data(104, 10) + np.random.seed(SEED) + data = Data(seed=SEED) + data.generate_mute_data(1000, 1) settings = { "cmi_estimator": "JidtKraskovCMI", "n_perm_max_stat": 21, @@ -41,10 +43,10 @@ def test_max_statistic_sequential(): "max_lag_target": 5, } setup = MultivariateTE() - setup._initialise(settings, data, sources=[0, 2], target=1) - setup.current_value = (1, 4) - setup.selected_vars_target = [(1, 1)] - setup.selected_vars_sources = [(0, 1), (2, 1)] + setup._initialise(settings, data, sources=[0, 1], target=3) + setup.current_value = (3, 4) + setup.selected_vars_target = [(3, 1)] + setup.selected_vars_sources = [(0, 1), (0, 2), (1, 1)] setup.selected_vars_full = setup.selected_vars_target + setup.selected_vars_sources setup._current_value_realisations = data.get_realisations( @@ -58,28 +60,39 @@ def test_max_statistic_sequential(): 0 ] setup_permuted = cp.deepcopy(setup) + data_permuted = cp.deepcopy(data) + # data_permuted._data[1, :, :] = np.random.rand(1, 1000, 1) + # data_permuted._data[1, :, :] = np.ones((1, 1000, 1)) setup_permuted._selected_vars_realisations = np.hstack( ( data.get_realisations( # use actual realisation for target var and first source var setup.current_value, - [setup.selected_vars_target[0], setup.selected_vars_sources[0]], + [setup.selected_vars_target[0]] + list(setup.selected_vars_sources[:2]), )[ 0 ], - np.random.rand( - data.n_realisations(setup.current_value), 1 - ), # use random data as realizations for second source var + # data_permuted._data[ + # 1, : data.n_realisations(setup.current_value), : + # ], # use random data as realizations for second source var + # np.ones((data.n_realisations(setup.current_value), 1)), + np.random.rand(data.n_realisations(setup.current_value), 1), ) ) - for s, expected in zip([setup, setup_permuted], [[True, True], [True, False]]): - [sign, p, te] = stats.max_statistic_sequential(analysis_setup=s, data=data) + for s, d, expected in zip( + [setup, setup_permuted], + [data, data_permuted], + [[True, True, True], [True, True, False]], + ): + [sign, p, te] = stats.max_statistic_sequential(analysis_setup=s, data=d) + print(te) print(p) print(sign) - print(te) assert len(p) == len(te) assert len(p) == len(sign) assert len(p) == len(setup.selected_vars_sources) - assert np.array_equal(sign, expected), "Incorrect sources inferred" + assert np.array_equal( + sign, expected + ), f"Incorrect sources inferred, expected: {expected}" setup = MultivariateTE() setup._initialise(settings, data, sources=[0, 1, 4], target=3) @@ -125,9 +138,9 @@ def test_max_statistic_sequential(): def test_max_statistic_sequential_bivariate(): - np.random.seed(0) - data = Data() - data.generate_mute_data(104, 10) + np.random.seed(SEED) + data = Data(seed=SEED) + data.generate_mute_data(1000, 1) settings = { "cmi_estimator": "JidtKraskovCMI", "n_perm_max_stat": 21, @@ -139,38 +152,38 @@ def test_max_statistic_sequential_bivariate(): "max_lag_target": 5, } setup = MultivariateTE() - setup._initialise(settings, data, sources=[0, 2], target=1) - setup.current_value = (1, 4) - setup.selected_vars_target = [(1, 1)] - setup.selected_vars_sources = [(0, 1), (2, 1)] + setup._initialise(settings, data, sources=[0, 1], target=3) + setup.current_value = (3, 4) + setup.selected_vars_target = [(3, 1)] + setup.selected_vars_sources = [(0, 1), (0, 2), (1, 1)] setup.selected_vars_full = setup.selected_vars_target + setup.selected_vars_sources - setup._current_value_realisations = data.get_realisations( setup.current_value, [setup.current_value] )[0] + setup._selected_vars_realisations = data.get_realisations( + setup.current_value, setup.selected_vars_full + )[0] - setup._selected_vars_realisations = np.hstack( - ( - data.get_realisations( # use actual realisation for target var and first source var - setup.current_value, - [setup.selected_vars_target[0], setup.selected_vars_sources[0]], - )[ - 0 - ], - np.random.rand( - data.n_realisations(setup.current_value), 1 - ), # use random data as realizations for second source var + # Bivariate sequential max stats collects source variable realizations from + # data object for running the test. + data_permuted = cp.deepcopy(data) + data_permuted._data[1, :, :] = np.random.randn(1, 1000, 1) + + for d, expected in zip( + [data, data_permuted], [[True, True, True], [True, True, False]] + ): + [sign, p, te] = stats.max_statistic_sequential_bivariate( + analysis_setup=setup, data=d ) - ) - [sign, p, te] = stats.max_statistic_sequential_bivariate( - analysis_setup=setup, data=data - ) - print(p) - print(sign) - print(te) - assert len(p) == len(te) - assert len(p) == len(sign) - assert len(p) == len(setup.selected_vars_sources) + print(te) + print(p) + print(sign) + assert len(p) == len(te) + assert len(p) == len(sign) + assert len(p) == len(setup.selected_vars_sources) + assert np.array_equal( + sign, expected + ), f"Incorrect sources inferred, expected: {expected}" setup = MultivariateTE() setup._initialise(settings, data, sources=[0, 1, 4], target=3) @@ -600,7 +613,7 @@ def test_analytical_surrogates(): # test_ais_fdr() # test_analytical_surrogates() # test_data_type() - test_network_fdr() + # test_network_fdr() # test_fdr_sorting() # test_find_pvalue() # test_find_table_max() @@ -611,4 +624,4 @@ def test_analytical_surrogates(): # test_max_statistic() # test_min_statistic() # test_max_statistic_sequential_bivariate() - # test_max_statistic_sequential() + test_max_statistic_sequential() From bfdbc1534f2d69b3b3fd75a4eb7656a2eb9d82be Mon Sep 17 00:00:00 2001 From: PWollstadtHRI Date: Mon, 12 Feb 2024 13:19:30 +0100 Subject: [PATCH 32/34] Fix inclusion of knn submodule during installation --- idtxl/knn/__init__.py | 0 setup.py | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 idtxl/knn/__init__.py diff --git a/idtxl/knn/__init__.py b/idtxl/knn/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/setup.py b/setup.py index 6197c07..7adedd6 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ setup( name="idtxl", - packages=["idtxl"], + packages=["idtxl", "idtxl/knn"], include_package_data=True, version="1.6", description="Information Dynamics Toolkit xl", From 975cfc736a0e58caddb8737ce4bc6be2e6c41ea1 Mon Sep 17 00:00:00 2001 From: Patricia Wollstadt Date: Mon, 25 Mar 2024 14:39:40 +0100 Subject: [PATCH 33/34] Fix conditioning in biv. sequential max stats Use the correct conditioning set for estimating the surrogate values. Before, only the remaining source vars instead of the remaining source vars and the target vars were used. Also, fix stats function for empty conditioning sets, which may occurr for bivariate MI estimation. Add and update unit tests to reflect fixes. --- idtxl/stats.py | 190 +++++++++++++++++++++++++-------------------- test/test_stats.py | 61 ++++++++++++++- 2 files changed, 163 insertions(+), 88 deletions(-) diff --git a/idtxl/stats.py b/idtxl/stats.py index f59a0e8..d5ac5b9 100644 --- a/idtxl/stats.py +++ b/idtxl/stats.py @@ -1,4 +1,5 @@ """Provide statistics functions.""" + # pylint: disable=protected-access import copy as cp @@ -594,15 +595,15 @@ def max_statistic_sequential(analysis_setup, data): and permute_in_time ): # Generate the surrogates analytically - surr_table[ - idx_c, : - ] = analysis_setup._cmi_estimator.estimate_surrogates_analytic( - n_perm=n_permutations, - var1=data.get_realisations(analysis_setup.current_value, [candidate])[ - 0 - ], - var2=analysis_setup._current_value_realisations, - conditional=conditional_realisations_current, + surr_table[idx_c, :] = ( + analysis_setup._cmi_estimator.estimate_surrogates_analytic( + n_perm=n_permutations, + var1=data.get_realisations( + analysis_setup.current_value, [candidate] + )[0], + var2=analysis_setup._current_value_realisations, + conditional=conditional_realisations_current, + ) ) else: analysis_setup.settings["analytical_surrogates"] = False @@ -750,8 +751,56 @@ def max_statistic_sequential_bivariate(analysis_setup, data): f"sequential maximum statistic, n_perm: {n_permutations}, testing {len(analysis_setup.selected_vars_sources)} selected sources" ) + def _allocate_memory_for_current_source(n_source_vars): + # Determine length of conditioning set and allocate memory. + if conditional_realisations_target is None: + if n_source_vars - 1 == 0: + conditional_realisations = None + else: + conditional_realisations = np.empty( + ( + data.n_realisations(analysis_setup.current_value) + * n_source_vars, + n_source_vars - 1, + ) + ).astype(data.data_type) + else: + conditional_realisations = np.empty( + ( + data.n_realisations(analysis_setup.current_value) * n_source_vars, + n_source_vars - 1 + len(analysis_setup.selected_vars_target), + ) + ).astype(data.data_type) + + candidate_realisations = np.empty( + (data.n_realisations(analysis_setup.current_value) * n_source_vars, 1) + ).astype(data.data_type) + return candidate_realisations, conditional_realisations + + def _get_current_cond_set(source_vars, candidate): + # Return conditioning set for current source variable. + realizations_remaining_source_vars = data.get_realisations( + analysis_setup.current_value, + set(source_vars).difference(set([candidate])), + )[0] + # The following may happen if either the requested conditioning is + # None or if the conditioning set that is tested consists only of + # a single candidate. + if realizations_remaining_source_vars is None: + # Returns None for no targets, i.e., an empty cond set + return conditional_realisations_target, ["var2", "conditional"] + if conditional_realisations_target is None: + return realizations_remaining_source_vars, ["var2"] + return np.hstack( + ( + realizations_remaining_source_vars, + conditional_realisations_target, + ) + ), ["var2"] + # Check if target variables were selected to distinguish between TE and MI - # analysis. + # analysis. We here only ever analyse results for a single target, so no + # need to separate target sources by target. if len(analysis_setup._selected_vars_target) == 0: conditional_realisations_target = None else: @@ -768,65 +817,35 @@ def max_statistic_sequential_bivariate(analysis_setup, data): significance = np.zeros(len(analysis_setup.selected_vars_sources)).astype(bool) pvalue = np.ones(len(analysis_setup.selected_vars_sources)) individual_stat = np.zeros(len(analysis_setup.selected_vars_sources)) + for source in significant_sources: + # Find selected past variables for current source source_vars = [ s for s in analysis_setup.selected_vars_sources if s[0] == source ] + candidate_realisations, conditional_realisations = ( + _allocate_memory_for_current_source(len(source_vars)) + ) - # Determine length of conditioning set and allocate memory. - if conditional_realisations_target is not None: - size_conditioning_set = len(source_vars) + len( - analysis_setup.selected_vars_target - ) - else: - size_conditioning_set = len(source_vars) - conditional_realisations = np.empty( - ( - data.n_realisations(analysis_setup.current_value) * len(source_vars), - size_conditioning_set - 1, - ) - ).astype(data.data_type) - candidate_realisations = np.empty( - (data.n_realisations(analysis_setup.current_value) * len(source_vars), 1) - ).astype(data.data_type) - - # Calculate TE/MI for each candidate in the conditional source set, - # i.e., calculate the conditional MI between each candidate and the - # current value, conditional on all selected variables in the - # conditioning set. Then sort the estimated TE/MI values. + # Calculate TE/MI for each candidate variable in the current source + # set, i.e., calculate the conditional MI between each candidate and + # the current value, conditional on all variables in the conditioning + # set. Then sort the estimated TE/MI values. i_1 = 0 i_2 = data.n_realisations(analysis_setup.current_value) surr_table = np.zeros((len(source_vars), n_permutations)) # Collect data for each candidate and the corresponding conditioning set. for idx_c, candidate in enumerate(source_vars): - realizations_remaining_source_vars = data.get_realisations( - analysis_setup.current_value, - set(source_vars).difference(set([candidate])), - )[0] - realizations_current_candidate = data.get_realisations( + + conditional_realisations_current, re_use = _get_current_cond_set( + source_vars, candidate + ) + if conditional_realisations is not None: + conditional_realisations[i_1:i_2,] = conditional_realisations_current + candidate_realisations[i_1:i_2,] = data.get_realisations( analysis_setup.current_value, [candidate] )[0] - # The following may happen if either the requested conditioning is - # 'none' or if the conditioning set that is tested consists only of - # a single candidate. - if realizations_remaining_source_vars is None: - conditional_realisations = conditional_realisations_target - re_use = ["var2", "conditional"] - else: - re_use = ["var2"] - if conditional_realisations_target is None: - conditional_realisations[ - i_1:i_2, - ] = realizations_remaining_source_vars - else: - conditional_realisations[i_1:i_2,] = np.hstack( - ( - realizations_remaining_source_vars, - conditional_realisations_target, - ) - ) - candidate_realisations[i_1:i_2,] = realizations_current_candidate i_1 = i_2 i_2 += data.n_realisations(analysis_setup.current_value) @@ -836,15 +855,15 @@ def max_statistic_sequential_bivariate(analysis_setup, data): and permute_in_time ): # Generate the surrogates analytically - surr_table[ - idx_c, : - ] = analysis_setup._cmi_estimator.estimate_surrogates_analytic( - n_perm=n_permutations, - var1=data.get_realisations( - analysis_setup.current_value, [candidate] - )[0], - var2=analysis_setup._current_value_realisations, - conditional=realizations_remaining_source_vars, + surr_table[idx_c, :] = ( + analysis_setup._cmi_estimator.estimate_surrogates_analytic( + n_perm=n_permutations, + var1=data.get_realisations( + analysis_setup.current_value, [candidate] + )[0], + var2=analysis_setup._current_value_realisations, + conditional=conditional_realisations_current, + ) ) else: analysis_setup.settings["analytical_surrogates"] = False @@ -856,22 +875,21 @@ def max_statistic_sequential_bivariate(analysis_setup, data): analysis_setup.settings, ) try: - surr_table[ - idx_c, : - ] = analysis_setup._cmi_estimator.estimate_parallel( - n_chunks=n_permutations, - re_use=["var2", "conditional"], - var1=surr_candidate_realisations, - var2=analysis_setup._current_value_realisations, - conditional=realizations_remaining_source_vars, + surr_table[idx_c, :] = ( + analysis_setup._cmi_estimator.estimate_parallel( + n_chunks=n_permutations, + re_use=["var2", "conditional"], + var1=surr_candidate_realisations, + var2=analysis_setup._current_value_realisations, + conditional=conditional_realisations_current, + ) ) except ex.AlgorithmExhaustedError as aee: - # The aglorithm cannot continue here, so - # we'll terminate the max sequential stats test, - # and declare all not significant + # The algorithm cannot continue here, so we'll terminate the max sequential + # stats test, and declare all not significant print( f"AlgorithmExhaustedError encountered in estimations: {aee.message}. " - "Stopping sequential max stats at candidate with rank 0" + "Stopping sequential max stats at candidate with rank 0." ) return ( np.zeros(len(analysis_setup.selected_vars_sources)).astype( @@ -1404,15 +1422,15 @@ def _create_surrogate_table( ): # Generate the surrogates analytically analysis_setup.settings["analytical_surrogates"] = True - surr_table[ - idx_c, : - ] = analysis_setup._cmi_estimator.estimate_surrogates_analytic( - n_perm=n_perm, - var1=data.get_realisations(analysis_setup.current_value, [candidate])[ - 0 - ], - var2=current_value_realisations, - conditional=conditional, + surr_table[idx_c, :] = ( + analysis_setup._cmi_estimator.estimate_surrogates_analytic( + n_perm=n_perm, + var1=data.get_realisations( + analysis_setup.current_value, [candidate] + )[0], + var2=current_value_realisations, + conditional=conditional, + ) ) else: analysis_setup.settings["analytical_surrogates"] = False diff --git a/test/test_stats.py b/test/test_stats.py index 0f56abd..d634035 100644 --- a/test/test_stats.py +++ b/test/test_stats.py @@ -1,4 +1,5 @@ """Unit tests for stats module.""" + # pylint: disable=protected-access import copy as cp @@ -151,6 +152,8 @@ def test_max_statistic_sequential_bivariate(): "min_lag_sources": 1, "max_lag_target": 5, } + + # Test bivariate TE setup = MultivariateTE() setup._initialise(settings, data, sources=[0, 1], target=3) setup.current_value = (3, 4) @@ -185,6 +188,7 @@ def test_max_statistic_sequential_bivariate(): sign, expected ), f"Incorrect sources inferred, expected: {expected}" + # Ensure no false positives on random data. setup = MultivariateTE() setup._initialise(settings, data, sources=[0, 1, 4], target=3) setup.current_value = (3, 4) @@ -232,6 +236,58 @@ def test_max_statistic_sequential_bivariate(): ), f"{p[i]} not smaller than critical alpha {setup.settings['alpha_max_seq']}" +def test_max_statistic_sequential_bivariate_mi(): + # Test bivariate sequential max stats when analyzing MI results. Here, the + # conditioning set is different than for TE estimation. + np.random.seed(SEED) + data = Data(seed=SEED) + data.generate_mute_data(1000, 1) + settings = { + "cmi_estimator": "JidtKraskovCMI", + "n_perm_max_stat": 21, + "n_perm_min_stat": 21, + "n_perm_omnibus": 21, + "n_perm_max_seq": 21, + "max_lag_sources": 5, + "min_lag_sources": 1, + "max_lag_target": 5, + } + # Test bivariate MI + setup = MultivariateTE() + setup._initialise(settings, data, sources=[0, 1], target=3) + setup.current_value = (3, 4) + setup.selected_vars_target = [] + setup.selected_vars_sources = [(0, 1), (0, 2), (3, 1)] + setup.selected_vars_full = setup.selected_vars_target + setup.selected_vars_sources + setup._current_value_realisations = data.get_realisations( + setup.current_value, [setup.current_value] + )[0] + setup._selected_vars_realisations = data.get_realisations( + setup.current_value, setup.selected_vars_full + )[0] + + # Bivariate sequential max stats collects source variable realizations from + # data object for running the test. + data_permuted = cp.deepcopy(data) + data_permuted._data[3, :, :] = np.random.randn(1, 1000, 1) + + for d, expected in zip( + [data, data_permuted], [[True, True, True], [True, True, False]] + ): + [sign, p, te] = stats.max_statistic_sequential_bivariate( + analysis_setup=setup, data=d + ) + print(te) + print(p) + print(sign) + assert len(p) == len(te) + assert len(p) == len(sign) + assert len(p) == len(setup.selected_vars_sources) + assert np.array_equal( + sign, expected + ), f"Incorrect sources inferred, expected: {expected}" + + def test_network_fdr(): # Simulate results for a 3-node network, analyzed all-to-all. Set the # omnibus p-value for targets 1 and 2 such that they does not survive @@ -623,5 +679,6 @@ def test_analytical_surrogates(): # test_omnibus_test() # test_max_statistic() # test_min_statistic() - # test_max_statistic_sequential_bivariate() - test_max_statistic_sequential() + test_max_statistic_sequential_bivariate_mi() + test_max_statistic_sequential_bivariate() + # test_max_statistic_sequential() From 1e5089dc88745afb7c93cef3ad6d6a2fc29985c6 Mon Sep 17 00:00:00 2001 From: Patricia Wollstadt Date: Mon, 25 Mar 2024 14:52:17 +0100 Subject: [PATCH 34/34] Fix formatting and pylint errors --- demos/demo_active_information_storage.py | 3 +- demos/demo_bivariate_mi.py | 15 +- demos/demo_bivariate_pid.py | 76 ++-- demos/demo_bivariate_te.py | 15 +- demos/demo_core_estimators.py | 14 +- demos/demo_hd_estimator.py | 36 +- demos/demo_multivariate_mi.py | 17 +- demos/demo_multivariate_pid.py | 423 +++++++++++++++++----- demos/demo_multivariate_te.py | 17 +- demos/demo_multivariate_te_mpi.py | 42 ++- demos/demo_significant_subgraph_mining.py | 58 +-- idtxl/estimator.py | 63 ++-- idtxl/network_inference.py | 11 +- 13 files changed, 522 insertions(+), 268 deletions(-) diff --git a/demos/demo_active_information_storage.py b/demos/demo_active_information_storage.py index c0f6d2f..b214a26 100644 --- a/demos/demo_active_information_storage.py +++ b/demos/demo_active_information_storage.py @@ -8,8 +8,7 @@ # b) Initialise analysis object and define settings network_analysis = ActiveInformationStorage() -settings = {'cmi_estimator': 'JidtGaussianCMI', - 'max_lag': 5} +settings = {"cmi_estimator": "JidtGaussianCMI", "max_lag": 5} # c) Run analysis results = network_analysis.analyse_network(settings=settings, data=data) diff --git a/demos/demo_bivariate_mi.py b/demos/demo_bivariate_mi.py index 25fe509..afb2c40 100644 --- a/demos/demo_bivariate_mi.py +++ b/demos/demo_bivariate_mi.py @@ -1,8 +1,9 @@ # Import classes +import matplotlib.pyplot as plt + from idtxl.bivariate_mi import BivariateMI from idtxl.data import Data from idtxl.visualise_graph import plot_network -import matplotlib.pyplot as plt # a) Generate test data data = Data() @@ -10,14 +11,16 @@ # b) Initialise analysis object and define settings network_analysis = BivariateMI() -settings = {'cmi_estimator': 'JidtGaussianCMI', - 'max_lag_sources': 5, - 'min_lag_sources': 1} +settings = { + "cmi_estimator": "JidtGaussianCMI", + "max_lag_sources": 5, + "min_lag_sources": 1, +} # c) Run analysis results = network_analysis.analyse_network(settings=settings, data=data) # d) Plot inferred network to console and via matplotlib -results.print_edge_list(weights='max_te_lag', fdr=False) -plot_network(results=results, weights='max_te_lag', fdr=False) +results.print_edge_list(weights="max_te_lag", fdr=False) +plot_network(results=results, weights="max_te_lag", fdr=False) plt.show() diff --git a/demos/demo_bivariate_pid.py b/demos/demo_bivariate_pid.py index f8ede06..ec40795 100644 --- a/demos/demo_bivariate_pid.py +++ b/demos/demo_bivariate_pid.py @@ -1,5 +1,6 @@ # Import classes import numpy as np + from idtxl.bivariate_pid import BivariatePID from idtxl.data import Data @@ -9,46 +10,61 @@ x = np.random.randint(0, alph, n) y = np.random.randint(0, alph, n) z = np.logical_xor(x, y).astype(int) -data = Data(np.vstack((x, y, z)), 'ps', normalise=False) +data = Data(np.vstack((x, y, z)), "ps", normalise=False) # b) Initialise analysis object and define settings for both PID estimators pid = BivariatePID() -settings_tartu = {'pid_estimator': 'TartuPID', 'lags_pid': [0, 0]} +settings_tartu = {"pid_estimator": "TartuPID", "lags_pid": [0, 0]} settings_sydney = { - 'alph_s1': alph, - 'alph_s2': alph, - 'alph_t': alph, - 'max_unsuc_swaps_row_parm': 60, - 'num_reps': 63, - 'max_iters': 1000, - 'pid_estimator': 'SydneyPID', - 'lags_pid': [0, 0]} + "alph_s1": alph, + "alph_s2": alph, + "alph_t": alph, + "max_unsuc_swaps_row_parm": 60, + "num_reps": 63, + "max_iters": 1000, + "pid_estimator": "SydneyPID", + "lags_pid": [0, 0], +} # c) Run Tartu estimator results_tartu = pid.analyse_single_target( - settings=settings_tartu, data=data, target=2, sources=[0, 1]) + settings=settings_tartu, data=data, target=2, sources=[0, 1] +) # d) Run Sydney estimator pid = BivariatePID() results_sydney = pid.analyse_single_target( - settings=settings_sydney, data=data, target=2, sources=[0, 1]) + settings=settings_sydney, data=data, target=2, sources=[0, 1] +) # e) Print results to console -print('\nLogical XOR') -print('Estimator Sydney\t\tTartu\t\tExpected\n') -print('Uni s1 {0:.4f}\t\t{1:.4f}\t\t{2:.2f}'.format( - results_sydney.get_single_target(2)['unq_s1'], - results_tartu.get_single_target(2)['unq_s1'], - 0)) -print('Uni s2 {0:.4f}\t\t{1:.4f}\t\t{2:.2f}'.format( - results_sydney.get_single_target(2)['unq_s2'], - results_tartu.get_single_target(2)['unq_s2'], - 0)) -print('Shared s1_s2 {0:.4f}\t\t{1:.4f}\t\t{2:.2f}'.format( - results_sydney.get_single_target(2)['shd_s1_s2'], - results_tartu.get_single_target(2)['shd_s1_s2'], - 0)) -print('Synergy s1_s2 {0:.4f}\t\t{1:.4f}\t\t{2:.2f}'.format( - results_sydney.get_single_target(2)['syn_s1_s2'], - results_tartu.get_single_target(2)['syn_s1_s2'], - 1)) +print("\nLogical XOR") +print("Estimator Sydney\t\tTartu\t\tExpected\n") +print( + "Uni s1 {0:.4f}\t\t{1:.4f}\t\t{2:.2f}".format( + results_sydney.get_single_target(2)["unq_s1"], + results_tartu.get_single_target(2)["unq_s1"], + 0, + ) +) +print( + "Uni s2 {0:.4f}\t\t{1:.4f}\t\t{2:.2f}".format( + results_sydney.get_single_target(2)["unq_s2"], + results_tartu.get_single_target(2)["unq_s2"], + 0, + ) +) +print( + "Shared s1_s2 {0:.4f}\t\t{1:.4f}\t\t{2:.2f}".format( + results_sydney.get_single_target(2)["shd_s1_s2"], + results_tartu.get_single_target(2)["shd_s1_s2"], + 0, + ) +) +print( + "Synergy s1_s2 {0:.4f}\t\t{1:.4f}\t\t{2:.2f}".format( + results_sydney.get_single_target(2)["syn_s1_s2"], + results_tartu.get_single_target(2)["syn_s1_s2"], + 1, + ) +) diff --git a/demos/demo_bivariate_te.py b/demos/demo_bivariate_te.py index 25bb8b4..892b8ff 100644 --- a/demos/demo_bivariate_te.py +++ b/demos/demo_bivariate_te.py @@ -1,8 +1,9 @@ # Import classes +import matplotlib.pyplot as plt + from idtxl.bivariate_te import BivariateTE from idtxl.data import Data from idtxl.visualise_graph import plot_network -import matplotlib.pyplot as plt # a) Generate test data data = Data() @@ -10,14 +11,16 @@ # b) Initialise analysis object and define settings network_analysis = BivariateTE() -settings = {'cmi_estimator': 'JidtGaussianCMI', - 'max_lag_sources': 5, - 'min_lag_sources': 1} +settings = { + "cmi_estimator": "JidtGaussianCMI", + "max_lag_sources": 5, + "min_lag_sources": 1, +} # c) Run analysis results = network_analysis.analyse_network(settings=settings, data=data) # d) Plot inferred network to console and via matplotlib -results.print_edge_list(weights='max_te_lag', fdr=False) -plot_network(results=results, weights='max_te_lag', fdr=False) +results.print_edge_list(weights="max_te_lag", fdr=False) +plot_network(results=results, weights="max_te_lag", fdr=False) plt.show() diff --git a/demos/demo_core_estimators.py b/demos/demo_core_estimators.py index 3d4d695..911c7f0 100644 --- a/demos/demo_core_estimators.py +++ b/demos/demo_core_estimators.py @@ -1,22 +1,24 @@ """Demonstrate the usage of IDTxl's core estimators.""" + import numpy as np -from idtxl.estimators_python import PythonKraskovCMI + from idtxl.estimators_jidt import ( JidtDiscreteAIS, JidtDiscreteCMI, JidtDiscreteMI, JidtDiscreteTE, - JidtKraskovAIS, - JidtKraskovCMI, - JidtKraskovMI, - JidtKraskovTE, JidtGaussianAIS, JidtGaussianCMI, JidtGaussianMI, JidtGaussianTE, + JidtKraskovAIS, + JidtKraskovCMI, + JidtKraskovMI, + JidtKraskovTE, ) -from idtxl.estimators_opencl import OpenCLKraskovMI, OpenCLKraskovCMI +from idtxl.estimators_opencl import OpenCLKraskovCMI, OpenCLKraskovMI from idtxl.estimators_pid import SydneyPID, TartuPID +from idtxl.estimators_python import PythonKraskovCMI from idtxl.idtxl_utils import calculate_mi # Generate Gaussian test data diff --git a/demos/demo_hd_estimator.py b/demos/demo_hd_estimator.py index a2728d5..352322d 100644 --- a/demos/demo_hd_estimator.py +++ b/demos/demo_hd_estimator.py @@ -12,15 +12,17 @@ >>> test_fast_emb.py """ + # Import classes import numpy as np + from idtxl.data_spiketime import Data_spiketime from idtxl.embedding_optimization_ais_Rudelt import OptimizationRudelt # a) Generate test data print("\nTest optimization_Rudelt using BBC estimator on Rudelt data") -data = Data_spiketime() # initialise empty data object -data.load_Rudelt_data() # load Rudelt spike time data +data = Data_spiketime() # initialise empty data object +data.load_Rudelt_data() # load Rudelt spike time data # b) Initialise analysis object and define settings # Run optimization with the Bayesian bias criterion (BBC) (alternatively, set @@ -29,20 +31,22 @@ # 'embedding_number_of_bins_set', and 'embedding_scaling_exponent_set' to # reduce the run time of this demo. Use defaults if unsure about the settings. settings = { - 'embedding_past_range_set': [0.005, 0.31548, 5.0], - 'embedding_number_of_bins_set': [1, 3, 5], - 'embedding_scaling_exponent_set': - {'number_of_scalings': 3, - 'min_first_bin_size': 0.005, - 'min_step_for_scaling': 0.01}, - 'number_of_bootstraps_R_max': 10, - 'number_of_bootstraps_R_tot': 10, - 'auto_MI_bin_size_set': [0.01, 0.025, 0.05, 0.25], - 'estimation_method': 'bbc', - 'debug': True, - 'visualization': True, - 'output_path': '.', - 'output_prefix': 'systemtest_optimizationRudelt_image1'} + "embedding_past_range_set": [0.005, 0.31548, 5.0], + "embedding_number_of_bins_set": [1, 3, 5], + "embedding_scaling_exponent_set": { + "number_of_scalings": 3, + "min_first_bin_size": 0.005, + "min_step_for_scaling": 0.01, + }, + "number_of_bootstraps_R_max": 10, + "number_of_bootstraps_R_tot": 10, + "auto_MI_bin_size_set": [0.01, 0.025, 0.05, 0.25], + "estimation_method": "bbc", + "debug": True, + "visualization": True, + "output_path": ".", + "output_prefix": "systemtest_optimizationRudelt_image1", +} hd_estimator = OptimizationRudelt(settings) # c) Run analysis and save a plot of the results to current directory. diff --git a/demos/demo_multivariate_mi.py b/demos/demo_multivariate_mi.py index 0bde929..311827b 100644 --- a/demos/demo_multivariate_mi.py +++ b/demos/demo_multivariate_mi.py @@ -1,8 +1,9 @@ # Import classes -from idtxl.multivariate_mi import MultivariateMI +import matplotlib.pyplot as plt + from idtxl.data import Data +from idtxl.multivariate_mi import MultivariateMI from idtxl.visualise_graph import plot_network -import matplotlib.pyplot as plt # a) Generate test data data = Data() @@ -10,14 +11,16 @@ # b) Initialise analysis object and define settings network_analysis = MultivariateMI() -settings = {'cmi_estimator': 'JidtGaussianCMI', - 'max_lag_sources': 5, - 'min_lag_sources': 1} +settings = { + "cmi_estimator": "JidtGaussianCMI", + "max_lag_sources": 5, + "min_lag_sources": 1, +} # c) Run analysis results = network_analysis.analyse_network(settings=settings, data=data) # d) Plot inferred network to console and via matplotlib -results.print_edge_list(weights='max_te_lag', fdr=False) -plot_network(results=results, weights='max_te_lag', fdr=False) +results.print_edge_list(weights="max_te_lag", fdr=False) +plot_network(results=results, weights="max_te_lag", fdr=False) plt.show() diff --git a/demos/demo_multivariate_pid.py b/demos/demo_multivariate_pid.py index ee119f4..ef4ef8f 100644 --- a/demos/demo_multivariate_pid.py +++ b/demos/demo_multivariate_pid.py @@ -1,7 +1,8 @@ # Import classes import numpy as np -from idtxl.multivariate_pid import MultivariatePID + from idtxl.data import Data +from idtxl.multivariate_pid import MultivariatePID # a) Generate test data n = 100 @@ -9,32 +10,55 @@ x = np.random.randint(0, alph, n) y = np.random.randint(0, alph, n) z = np.logical_xor(x, y).astype(int) -data = Data(np.vstack((x, y, z)), 'ps', normalise=False) +data = Data(np.vstack((x, y, z)), "ps", normalise=False) # b) Initialise analysis object and define settings for SxPID estimators pid = MultivariatePID() -settings_SxPID = {'pid_estimator': 'SxPID', 'lags_pid': [0, 0]} +settings_SxPID = {"pid_estimator": "SxPID", "lags_pid": [0, 0]} # c) Run Goettingen estimator results_SxPID = pid.analyse_single_target( - settings=settings_SxPID, data=data, target=2, sources=[0, 1]) + settings=settings_SxPID, data=data, target=2, sources=[0, 1] +) # e) Print results to console -print('\nLogical XOR') -print('Estimator SxPID\t\tExpected\n') -print('Uni s1 {0:.4f}\t\t{1:.4f}'.format( - results_SxPID.get_single_target(2)['avg'][((1,),)][2], - .5896)) -print('Uni s2 {0:.4f}\t\t{1:.4f}'.format( - results_SxPID.get_single_target(2)['avg'][((2,),)][2], - 0.5896)) -print('Shared s1_s2 {0:.4f}\t\t{1:.4f}'.format( - results_SxPID.get_single_target(2)['avg'][((1,),(2,),)][2], - -0.5896)) -print('Synergy s1_s2 {0:.4f}\t\t{1:.4f}'.format( - results_SxPID.get_single_target(2)['avg'][((1,2,),)][2], - 0.415)) +print("\nLogical XOR") +print("Estimator SxPID\t\tExpected\n") +print( + "Uni s1 {0:.4f}\t\t{1:.4f}".format( + results_SxPID.get_single_target(2)["avg"][((1,),)][2], 0.5896 + ) +) +print( + "Uni s2 {0:.4f}\t\t{1:.4f}".format( + results_SxPID.get_single_target(2)["avg"][((2,),)][2], 0.5896 + ) +) +print( + "Shared s1_s2 {0:.4f}\t\t{1:.4f}".format( + results_SxPID.get_single_target(2)["avg"][ + ( + (1,), + (2,), + ) + ][2], + -0.5896, + ) +) +print( + "Synergy s1_s2 {0:.4f}\t\t{1:.4f}".format( + results_SxPID.get_single_target(2)["avg"][ + ( + ( + 1, + 2, + ), + ) + ][2], + 0.415, + ) +) # Some special Examples @@ -42,104 +66,309 @@ x = np.asarray([0, 1, 0, 2]) y = np.asarray([1, 0, 2, 0]) z = np.asarray([1, 1, 2, 2]) -data = Data(np.vstack((x, y, z)), 'ps', normalise=False) +data = Data(np.vstack((x, y, z)), "ps", normalise=False) pid = MultivariatePID() -settings_SxPID = {'pid_estimator': 'SxPID', 'lags_pid': [0, 0]} +settings_SxPID = {"pid_estimator": "SxPID", "lags_pid": [0, 0]} results_SxPID = pid.analyse_single_target( - settings=settings_SxPID, data=data, target=2, sources=[0, 1]) - -print('\nLogical PwUnq') -print('Estimator SxPID\t\tExpected\n') -print('Uni s1 {0:.4f}\t\t{1:.1f}'.format( - results_SxPID.get_single_target(2)['avg'][((1,),)][2], - .5)) -print('Uni s2 {0:.4f}\t\t{1:.1f}'.format( - results_SxPID.get_single_target(2)['avg'][((2,),)][2], - 0.5)) -print('Shared s1_s2 {0:.4f}\t\t{1:.1f}'.format( - results_SxPID.get_single_target(2)['avg'][((1,),(2,),)][2], - 0.)) -print('Synergy s1_s2 {0:.4f}\t\t{1:.1f}'.format( - results_SxPID.get_single_target(2)['avg'][((1,2,),)][2], - 0.)) + settings=settings_SxPID, data=data, target=2, sources=[0, 1] +) + +print("\nLogical PwUnq") +print("Estimator SxPID\t\tExpected\n") +print( + "Uni s1 {0:.4f}\t\t{1:.1f}".format( + results_SxPID.get_single_target(2)["avg"][((1,),)][2], 0.5 + ) +) +print( + "Uni s2 {0:.4f}\t\t{1:.1f}".format( + results_SxPID.get_single_target(2)["avg"][((2,),)][2], 0.5 + ) +) +print( + "Shared s1_s2 {0:.4f}\t\t{1:.1f}".format( + results_SxPID.get_single_target(2)["avg"][ + ( + (1,), + (2,), + ) + ][2], + 0.0, + ) +) +print( + "Synergy s1_s2 {0:.4f}\t\t{1:.1f}".format( + results_SxPID.get_single_target(2)["avg"][ + ( + ( + 1, + 2, + ), + ) + ][2], + 0.0, + ) +) # Redundancy Error x = np.asarray([0, 0, 0, 1, 1, 1, 0, 1]) y = np.asarray([0, 0, 0, 1, 1, 1, 1, 0]) z = np.asarray([0, 0, 0, 1, 1, 1, 0, 1]) -data = Data(np.vstack((x, y, z)), 'ps', normalise=False) +data = Data(np.vstack((x, y, z)), "ps", normalise=False) pid = MultivariatePID() -settings_SxPID = {'pid_estimator': 'SxPID', 'lags_pid': [0, 0]} +settings_SxPID = {"pid_estimator": "SxPID", "lags_pid": [0, 0]} results_SxPID = pid.analyse_single_target( - settings=settings_SxPID, data=data, target=2, sources=[0, 1]) - -print('\nLogical RndErr') -print('Estimator SxPID\t\tExpected\n') -print('Uni s1 {0:.4f}\t\t{1:.4f}'.format( - results_SxPID.get_single_target(2)['avg'][((1,),)][2], - .4433)) -print('Uni s2 {0:.4f}\t\t{1:.4f}'.format( - results_SxPID.get_single_target(2)['avg'][((2,),)][2], - -0.368)) -print('Shared s1_s2 {0:.4f}\t\t{1:.4f}'.format( - results_SxPID.get_single_target(2)['avg'][((1,),(2,),)][2], - 0.5567)) -print('Synergy s1_s2 {0:.4f}\t\t{1:.4f}'.format( - results_SxPID.get_single_target(2)['avg'][((1,2,),)][2], - 0.368)) + settings=settings_SxPID, data=data, target=2, sources=[0, 1] +) + +print("\nLogical RndErr") +print("Estimator SxPID\t\tExpected\n") +print( + "Uni s1 {0:.4f}\t\t{1:.4f}".format( + results_SxPID.get_single_target(2)["avg"][((1,),)][2], 0.4433 + ) +) +print( + "Uni s2 {0:.4f}\t\t{1:.4f}".format( + results_SxPID.get_single_target(2)["avg"][((2,),)][2], -0.368 + ) +) +print( + "Shared s1_s2 {0:.4f}\t\t{1:.4f}".format( + results_SxPID.get_single_target(2)["avg"][ + ( + (1,), + (2,), + ) + ][2], + 0.5567, + ) +) +print( + "Synergy s1_s2 {0:.4f}\t\t{1:.4f}".format( + results_SxPID.get_single_target(2)["avg"][ + ( + ( + 1, + 2, + ), + ) + ][2], + 0.368, + ) +) # Three bits hash s1 = np.asarray([0, 0, 0, 0, 1, 1, 1, 1]) s2 = np.asarray([0, 0, 1, 1, 0, 0, 1, 1]) s3 = np.asarray([0, 1, 0, 1, 0, 1, 0, 1]) -z = np.asarray([0, 1, 1, 0, 1, 0, 0, 1]) -data = Data(np.vstack((s1, s2, s3, z)), 'ps', normalise=False) +z = np.asarray([0, 1, 1, 0, 1, 0, 0, 1]) +data = Data(np.vstack((s1, s2, s3, z)), "ps", normalise=False) pid = MultivariatePID() -settings_SxPID = {'pid_estimator': 'SxPID', 'lags_pid': [0, 0, 0]} +settings_SxPID = {"pid_estimator": "SxPID", "lags_pid": [0, 0, 0]} results_SxPID = pid.analyse_single_target( - settings=settings_SxPID, data=data, target=3, sources=[0, 1, 2]) - -print('\nLogical PwUnq') -print('Estimator SxPID\t\tExpected\n') -print('Uni s1 {0:.4f}\t\t{1:.4f}'.format( - results_SxPID.get_single_target(3)['avg'][((1,),)][2], 0.3219)) -print('Uni s2 {0:.4f}\t\t{1:.4f}'.format( - results_SxPID.get_single_target(3)['avg'][((2,),)][2], 0.3219)) -print('Uni s3 {0:.4f}\t\t{1:.4f}'.format( - results_SxPID.get_single_target(3)['avg'][((3,),)][2], 0.3219)) -print('Synergy s1_s2_s3 {0:.4f}\t\t{1:.4f}'.format( - results_SxPID.get_single_target(3)['avg'][((1,2,3),)][2], 0.2451)) -print('Synergy s1_s2 {0:.4f}\t\t{1:.4f}'.format( - results_SxPID.get_single_target(3)['avg'][((1,2,),)][2], 0.1699)) -print('Synergy s1_s3 {0:.4f}\t\t{1:.4f}'.format( - results_SxPID.get_single_target(3)['avg'][((1,3,),)][2], 0.1699)) -print('Synergy s2_s3 {0:.4f}\t\t{1:.4f}'.format( - results_SxPID.get_single_target(3)['avg'][((2,3,),)][2], 0.1699)) -print('Shared s1_s2_s3 {0:.4f}\t\t{1:.4f}'.format( - results_SxPID.get_single_target(3)['avg'][((1,),(2,),(3,),)][2], 0.1926)) -print('Shared of (s1, s2) {0:.4f}\t\t{1:.4f}'.format( - results_SxPID.get_single_target(3)['avg'][((1,), (2,),)][2], -0.1926)) -print('Shared of (s1, s2) {0:.4f}\t\t{1:.4f}'.format( - results_SxPID.get_single_target(3)['avg'][((1,), (3,),)][2], -0.1926)) -print('Shared of (s2, s3) {0:.4f}\t\t{1:.4f}'.format( - results_SxPID.get_single_target(3)['avg'][((2,), (3,),)][2], -0.1926)) -print('Shared of (Synergy s1_s2, Synergy s1_s3) {0:.4f}\t\t{1:.4f}'.format( - results_SxPID.get_single_target(3)['avg'][((1,2,), (1,3),)][2], 0.0931)) -print('Shared of (Synergy s1_s2, Synergy s2_s3) {0:.4f}\t\t{1:.4f}'.format( - results_SxPID.get_single_target(3)['avg'][((1,2,), (2,3),)][2], 0.0931)) -print('Shared of (Synergy s1_s3, Synergy s2_s3) {0:.4f}\t\t{1:.4f}'.format( - results_SxPID.get_single_target(3)['avg'][((1,3,), (2,3),)][2], 0.0931)) -print('Shared of (Synergy s1_s2, Synergy s1_s3, Synergy s2_s3) {0:.4f}\t\t{1:.4f}'.format( - results_SxPID.get_single_target(3)['avg'][((1,2,), (1,3), (2,3),)][2], -0.2268)) -print('Shared of (s1, Synergy s2_s3) {0:.4f}\t\t{1:.4f}'.format( - results_SxPID.get_single_target(3)['avg'][((1,), (2,3),)][2], -0.1292)) -print('Shared of (s2, Synergy s1_s3) {0:.4f}\t\t{1:.4f}'.format( - results_SxPID.get_single_target(3)['avg'][((2,), (1,3),)][2], -0.1292)) -print('Shared of (s3, Synergy s1_s2) {0:.4f}\t\t{1:.4f}'.format( - results_SxPID.get_single_target(3)['avg'][((3,), (1,2),)][2], -0.1292)) + settings=settings_SxPID, data=data, target=3, sources=[0, 1, 2] +) + +print("\nLogical PwUnq") +print("Estimator SxPID\t\tExpected\n") +print( + "Uni s1 {0:.4f}\t\t{1:.4f}".format( + results_SxPID.get_single_target(3)["avg"][((1,),)][2], 0.3219 + ) +) +print( + "Uni s2 {0:.4f}\t\t{1:.4f}".format( + results_SxPID.get_single_target(3)["avg"][((2,),)][2], 0.3219 + ) +) +print( + "Uni s3 {0:.4f}\t\t{1:.4f}".format( + results_SxPID.get_single_target(3)["avg"][((3,),)][2], 0.3219 + ) +) +print( + "Synergy s1_s2_s3 {0:.4f}\t\t{1:.4f}".format( + results_SxPID.get_single_target(3)["avg"][((1, 2, 3),)][2], 0.2451 + ) +) +print( + "Synergy s1_s2 {0:.4f}\t\t{1:.4f}".format( + results_SxPID.get_single_target(3)["avg"][ + ( + ( + 1, + 2, + ), + ) + ][2], + 0.1699, + ) +) +print( + "Synergy s1_s3 {0:.4f}\t\t{1:.4f}".format( + results_SxPID.get_single_target(3)["avg"][ + ( + ( + 1, + 3, + ), + ) + ][2], + 0.1699, + ) +) +print( + "Synergy s2_s3 {0:.4f}\t\t{1:.4f}".format( + results_SxPID.get_single_target(3)["avg"][ + ( + ( + 2, + 3, + ), + ) + ][2], + 0.1699, + ) +) +print( + "Shared s1_s2_s3 {0:.4f}\t\t{1:.4f}".format( + results_SxPID.get_single_target(3)["avg"][ + ( + (1,), + (2,), + (3,), + ) + ][2], + 0.1926, + ) +) +print( + "Shared of (s1, s2) {0:.4f}\t\t{1:.4f}".format( + results_SxPID.get_single_target(3)["avg"][ + ( + (1,), + (2,), + ) + ][2], + -0.1926, + ) +) +print( + "Shared of (s1, s2) {0:.4f}\t\t{1:.4f}".format( + results_SxPID.get_single_target(3)["avg"][ + ( + (1,), + (3,), + ) + ][2], + -0.1926, + ) +) +print( + "Shared of (s2, s3) {0:.4f}\t\t{1:.4f}".format( + results_SxPID.get_single_target(3)["avg"][ + ( + (2,), + (3,), + ) + ][2], + -0.1926, + ) +) +print( + "Shared of (Synergy s1_s2, Synergy s1_s3) {0:.4f}\t\t{1:.4f}".format( + results_SxPID.get_single_target(3)["avg"][ + ( + ( + 1, + 2, + ), + (1, 3), + ) + ][2], + 0.0931, + ) +) +print( + "Shared of (Synergy s1_s2, Synergy s2_s3) {0:.4f}\t\t{1:.4f}".format( + results_SxPID.get_single_target(3)["avg"][ + ( + ( + 1, + 2, + ), + (2, 3), + ) + ][2], + 0.0931, + ) +) +print( + "Shared of (Synergy s1_s3, Synergy s2_s3) {0:.4f}\t\t{1:.4f}".format( + results_SxPID.get_single_target(3)["avg"][ + ( + ( + 1, + 3, + ), + (2, 3), + ) + ][2], + 0.0931, + ) +) +print( + "Shared of (Synergy s1_s2, Synergy s1_s3, Synergy s2_s3) {0:.4f}\t\t{1:.4f}".format( + results_SxPID.get_single_target(3)["avg"][ + ( + ( + 1, + 2, + ), + (1, 3), + (2, 3), + ) + ][2], + -0.2268, + ) +) +print( + "Shared of (s1, Synergy s2_s3) {0:.4f}\t\t{1:.4f}".format( + results_SxPID.get_single_target(3)["avg"][ + ( + (1,), + (2, 3), + ) + ][2], + -0.1292, + ) +) +print( + "Shared of (s2, Synergy s1_s3) {0:.4f}\t\t{1:.4f}".format( + results_SxPID.get_single_target(3)["avg"][ + ( + (2,), + (1, 3), + ) + ][2], + -0.1292, + ) +) +print( + "Shared of (s3, Synergy s1_s2) {0:.4f}\t\t{1:.4f}".format( + results_SxPID.get_single_target(3)["avg"][ + ( + (3,), + (1, 2), + ) + ][2], + -0.1292, + ) +) diff --git a/demos/demo_multivariate_te.py b/demos/demo_multivariate_te.py index 5fc1f0a..b9f9e0e 100644 --- a/demos/demo_multivariate_te.py +++ b/demos/demo_multivariate_te.py @@ -1,8 +1,9 @@ # Import classes -from idtxl.multivariate_te import MultivariateTE +import matplotlib.pyplot as plt + from idtxl.data import Data +from idtxl.multivariate_te import MultivariateTE from idtxl.visualise_graph import plot_network -import matplotlib.pyplot as plt # a) Generate test data data = Data() @@ -10,14 +11,16 @@ # b) Initialise analysis object and define settings network_analysis = MultivariateTE() -settings = {'cmi_estimator': 'JidtGaussianCMI', - 'max_lag_sources': 5, - 'min_lag_sources': 1} +settings = { + "cmi_estimator": "JidtGaussianCMI", + "max_lag_sources": 5, + "min_lag_sources": 1, +} # c) Run analysis results = network_analysis.analyse_network(settings=settings, data=data) # d) Plot inferred network to console and via matplotlib -results.print_edge_list(weights='max_te_lag', fdr=False) -plot_network(results=results, weights='max_te_lag', fdr=False) +results.print_edge_list(weights="max_te_lag", fdr=False) +plot_network(results=results, weights="max_te_lag", fdr=False) plt.show() diff --git a/demos/demo_multivariate_te_mpi.py b/demos/demo_multivariate_te_mpi.py index e88dfdb..9af5ef2 100644 --- a/demos/demo_multivariate_te_mpi.py +++ b/demos/demo_multivariate_te_mpi.py @@ -1,13 +1,15 @@ # Import classes -from idtxl.multivariate_te import MultivariateTE -from idtxl.data import Data -from idtxl.visualise_graph import plot_network -import matplotlib.pyplot as plt -import time import sys +import time +import matplotlib.pyplot as plt from mpi4py import MPI +from idtxl.data import Data +from idtxl.multivariate_te import MultivariateTE +from idtxl.visualise_graph import plot_network + + def main(args): """Demo of how to use the MPIEstimator for network analysis Call this script using @@ -15,16 +17,16 @@ def main(args): for systems supporting MPI Spawn (MPI version >= 2) or mpiexec -n python -m mpi4py.futures demo/demo_multivariate_te_mpi.py for legacy MPI 1 implementations. - + Call python demos/demo_multivariate_te_mpi.py 0 for a comparison without MPI. """ - assert MPI.COMM_WORLD.Get_rank() == 0 + assert MPI.COMM_WORLD.Get_rank() == 0 max_workers = int(args[1]) - print(f'Running TE Test with {max_workers} MPI workers.') + print(f"Running TE Test with {max_workers} MPI workers.") # a) Generate test data data = Data(seed=12345) @@ -32,23 +34,25 @@ def main(args): # b) Initialise analysis object and define settings network_analysis = MultivariateTE() - settings = {'cmi_estimator': 'JidtGaussianCMI', - 'max_lag_sources': 5, - 'min_lag_sources': 1, - 'MPI': max_workers > 0, - 'max_workers': max_workers - } + settings = { + "cmi_estimator": "JidtGaussianCMI", + "max_lag_sources": 5, + "min_lag_sources": 1, + "MPI": max_workers > 0, + "max_workers": max_workers, + } # c) Run analysis start = time.time() results = network_analysis.analyse_network(settings=settings, data=data) end = time.time() - print(f'On {max_workers} workers, the task took {end - start} seconds.') + print(f"On {max_workers} workers, the task took {end - start} seconds.") # d) Plot inferred network to console and via matplotlib - results.print_edge_list(weights='max_te_lag', fdr=False) - plot_network(results=results, weights='max_te_lag', fdr=False) + results.print_edge_list(weights="max_te_lag", fdr=False) + plot_network(results=results, weights="max_te_lag", fdr=False) plt.show() -if __name__ == '__main__': - main(sys.argv) \ No newline at end of file + +if __name__ == "__main__": + main(sys.argv) diff --git a/demos/demo_significant_subgraph_mining.py b/demos/demo_significant_subgraph_mining.py index a7f810f..6455f72 100644 --- a/demos/demo_significant_subgraph_mining.py +++ b/demos/demo_significant_subgraph_mining.py @@ -11,21 +11,21 @@ data_b = Data() data_b.generate_mute_data(100, 5) settings = { - 'cmi_estimator': 'JidtGaussianCMI', - 'n_perm_max_stat': 50, - 'n_perm_min_stat': 50, - 'n_perm_omnibus': 200, - 'n_perm_max_seq': 50, - 'max_lag_target': 5, - 'max_lag_sources': 5, - 'min_lag_sources': 1, - 'permute_in_time': True - } + "cmi_estimator": "JidtGaussianCMI", + "n_perm_max_stat": 50, + "n_perm_min_stat": 50, + "n_perm_omnibus": 200, + "n_perm_max_seq": 50, + "max_lag_target": 5, + "max_lag_sources": 5, + "min_lag_sources": 1, + "permute_in_time": True, +} # Run analysis with different targets to simulate different results for both # analyses. network_analysis = MultivariateTE() -res_a = network_analysis.analyse_network(settings, data_a, sources='all') -res_b = network_analysis.analyse_network(settings, data_b, sources='all') +res_a = network_analysis.analyse_network(settings, data_a, sources="all") +res_b = network_analysis.analyse_network(settings, data_b, sources="all") # Perform significant subgraph mining on inferred networks using different # correction methods. Use the IDTxl format as input, which looks also at the @@ -36,13 +36,13 @@ [res_b.get_source_variables(fdr=False) for n in range(sample_size)], design="between", alpha=0.05, - data_format="idtxl" - ) + data_format="idtxl", +) res_ssm_between = SSM_between.enumerate_significant_subgraphs( method="Hommel", # correction method: "Tarone", "Hommel", or "Westfall-Young" verbose=True, num_perm=10000, - max_depth=np.infty + max_depth=np.infty, ) # List of subgraphs, each entry is a tuple comprising a list of edges and the # p-value for this subgraph. @@ -53,14 +53,14 @@ [res_b.get_source_variables(fdr=False) for n in range(sample_size)], design="within", alpha=0.05, - data_format="idtxl" - ) + data_format="idtxl", +) res_ssm_within = SSM_within.enumerate_significant_subgraphs( method="Westfall-Young", # correction method: "Tarone", "Hommel", or "Westfall-Young" wy_algorithm="simple_depth_first", verbose=True, num_perm=10000, - max_depth=np.infty + max_depth=np.infty, ) print(res_ssm_within) @@ -69,29 +69,35 @@ [res_b.get_source_variables(fdr=False) for n in range(sample_size)], design="within", alpha=0.05, - data_format="idtxl" - ) + data_format="idtxl", +) res_ssm_within = SSM_within.enumerate_significant_subgraphs( method="Tarone", # correction method: "Tarone", "Hommel", or "Westfall-Young" verbose=True, num_perm=10000, - max_depth=np.infty + max_depth=np.infty, ) print(res_ssm_within) # Use adjacency matrices as input. This ignores the time dimension of the # inferred network. SSM_within = SignificantSubgraphMining( - [res_a.get_adjacency_matrix(weights='binary', fdr=False).edge_matrix for n in range(sample_size)], - [res_b.get_adjacency_matrix(weights='binary', fdr=False).edge_matrix for n in range(sample_size)], + [ + res_a.get_adjacency_matrix(weights="binary", fdr=False).edge_matrix + for n in range(sample_size) + ], + [ + res_b.get_adjacency_matrix(weights="binary", fdr=False).edge_matrix + for n in range(sample_size) + ], design="within", alpha=0.05, - data_format="adjacency" - ) + data_format="adjacency", +) res_ssm_within = SSM_within.enumerate_significant_subgraphs( method="Tarone", # correction method: "Tarone", "Hommel", or "Westfall-Young" verbose=True, num_perm=10000, - max_depth=np.infty + max_depth=np.infty, ) print(res_ssm_within) diff --git a/idtxl/estimator.py b/idtxl/estimator.py index 46ba078..e531bf5 100644 --- a/idtxl/estimator.py +++ b/idtxl/estimator.py @@ -1,11 +1,12 @@ """Provide estimator base class for information theoretic measures.""" -import os + import importlib import inspect -from pprint import pprint +import os from abc import ABCMeta, abstractmethod +from pprint import pprint + import numpy as np -from . import idtxl_exceptions as ex MODULE_EXTENSIONS = ".py" # ('.py', '.pyc', '.pyo') ESTIMATOR_PREFIX = "estimators_" @@ -54,10 +55,10 @@ def _find_estimator(est): # constraint may be relaxed in the future. if not np.issubclass_(est, Estimator): raise RuntimeError( - "Provided class should implement abstract class" " Estimator." + "Provided class should implement abstract class Estimator." ) return est - elif type(est) is str: + if isinstance(est, str): module_list = _package_contents() estimator = None for m in module_list: @@ -67,11 +68,10 @@ def _find_estimator(est): except AttributeError: pass if not estimator: - raise RuntimeError("Estimator {0} not found.".format(est)) + raise RuntimeError(f"Estimator {est} not found.") else: raise TypeError( - "Please provide an estimator class or the name of an " - "estimator as string." + "Please provide an estimator class or the name of an estimator as string." ) @@ -197,7 +197,7 @@ def _check_settings(self, settings=None): """ if settings is None: return {} - elif type(settings) is not dict: + elif not isinstance(settings, dict): raise TypeError("settings should be a dictionary.") else: return settings @@ -206,20 +206,15 @@ def _check_number_of_points(self, n_points): """Sanity check for number of points going into the estimator.""" if (n_points - 1) <= int(self.settings["kraskov_k"]): raise RuntimeError( - "Insufficient number of points ({0}) for the " - "requested number of nearest neighbours " - "(kraskov_k: {1}).".format(n_points, self.settings["kraskov_k"]) + f"Insufficient number of points ({n_points}) for the requested number of nearest neighbours " + f"(kraskov_k: {self.settings['kraskov_k']})." ) if (n_points - 1) <= ( int(self.settings["kraskov_k"]) + int(self.settings["theiler_t"]) ): raise RuntimeError( - "Insufficient number of points ({0}) for the " - "requested number of nearest neighbours " - "(kraskov_k: {1}) and Theiler-correction " - "(theiler_t: {2}).".format( - n_points, self.settings["kraskov_k"], self.settings["theiler_t"] - ) + f"Insufficient number of points ({n_points}) for the requested number of nearest neighbours (kraskov_k:" + f" {self.settings['kraskov_k']}) and Theiler-correction (theiler_t: {self.settings['theiler_t']})." ) def _ensure_one_dim_input(self, var): @@ -239,7 +234,7 @@ def _ensure_one_dim_input(self, var): else: raise TypeError("2D input arrays must have shape[1] == 1.") elif len(var.shape) > 2: - raise TypeError("Input arrays must be 1D or 2D with shape[1] == " "1.") + raise TypeError("Input arrays must be 1D or 2D with shape[1] == 1.") return var def _ensure_two_dim_input(self, var): @@ -334,20 +329,14 @@ def estimate_parallel(self, n_chunks=1, re_use=None, **data): n_samples_total.append(data[v].shape[0]) assert ( np.array(n_samples_total) == n_samples_total[0] - ).all(), "No. realisations should be the same for all variables: " "{0}".format( - n_samples_total - ) + ).all(), f"No. realisations should be the same for all variables: {n_samples_total}" n_samples_total = n_samples_total[0] assert ( n_samples_total is not None ), "All variables provided for estimation are empty." assert n_samples_total % n_chunks == 0, ( - "No. chunks ({0}) does not match data length ({1}). Remainder:" - " {2}.".format( - n_chunks, - data[slice_vars[0]].shape[0], - data[slice_vars[0]].shape[0] % n_chunks, - ) + f"No. chunks ({n_chunks}) does not match data length ({data[slice_vars[0]].shape[0]}). " + f"Remainder: {data[slice_vars[0]].shape[0] % n_chunks}." ) # Cut data into chunks and call estimator serially on each chunk. @@ -357,12 +346,9 @@ def estimate_parallel(self, n_chunks=1, re_use=None, **data): results = np.empty((n_chunks)) for i in range(n_chunks): chunk_data = {} - # Slice data into single chunks - for ( - v - ) in ( - slice_vars - ): # NOTE: I am consciously not creating a deep copy here to save memory + # Slice data into single chunks. NOTE: I am consciously not + # creating a deep copy here to save memory + for v in slice_vars: if data[v] is not None: chunk_data[v] = data[v][idx_1:idx_2, :] else: @@ -370,12 +356,9 @@ def estimate_parallel(self, n_chunks=1, re_use=None, **data): # Collect data that is reused over chunks. for v in re_use: if data[v] is not None: - assert data[v].shape[0] == chunk_size, ( - "No. samples in variable {0} ({1}) is not equal " - "to chunk size ({2}).".format( - v, data[v].shape[0], chunk_size - ) - ) + assert ( + data[v].shape[0] == chunk_size + ), f"No. samples in variable {v} ({data[v].shape[0]}) is not equal to chunk size ({chunk_size})." chunk_data[v] = data[v] results[i] = self.estimate(**chunk_data) idx_1 = idx_2 diff --git a/idtxl/network_inference.py b/idtxl/network_inference.py index 7f888b3..1103896 100755 --- a/idtxl/network_inference.py +++ b/idtxl/network_inference.py @@ -1,4 +1,5 @@ """Parent class for all network inference.""" + import numpy as np from . import idtxl_exceptions as ex @@ -1080,7 +1081,7 @@ def _prune_candidates(self, data): self.current_value, remaining_candidates )[0] try: - [significant, p, surr_table] = stats.min_statistic( + significant, p, surr_table = stats.min_statistic( self, data, self.selected_vars_sources, @@ -1131,9 +1132,7 @@ def _test_final_conditional(self, data): else: if self.settings["verbose"]: print( - "selected variables: {0}".format( - self._idx_to_lag(self.selected_vars_full) - ) + f"selected variables: {self._idx_to_lag(self.selected_vars_full)}" ) try: [s, p, stat] = stats.omnibus_test(self, data) @@ -1141,8 +1140,8 @@ def _test_final_conditional(self, data): # The algorithm cannot continue here, so # we'll set the results to zero print( - "AlgorithmExhaustedError encountered in estimations: {}. " - "Halting current estimation set.".format(aee.message) + "AlgorithmExhaustedError encountered in estimations: " + f"{aee.message}. Halting current estimation set." ) # For now we don't need a stack trace: # traceback.print_tb(aee.__traceback__)