From 4a65931870adf9e9c666dd9525653e7c64c1a4d2 Mon Sep 17 00:00:00 2001 From: Patricia Wollstadt Date: Mon, 15 Jan 2024 15:27:23 +0100 Subject: [PATCH] Update sphinx-generated docs --- docs/doctrees/environment.pickle | Bin 923773 -> 921680 bytes docs/doctrees/idtxl.doctree | Bin 1835072 -> 1843274 bytes docs/doctrees/idtxl_data_class.doctree | Bin 170574 -> 170576 bytes docs/doctrees/idtxl_estimators.doctree | Bin 415198 -> 423381 bytes docs/doctrees/idtxl_results_class.doctree | Bin 149073 -> 149071 bytes docs/html/.buildinfo | 2 +- .../idtxl/active_information_storage.html | 371 +++--- docs/html/_modules/idtxl/bivariate_mi.html | 116 +- docs/html/_modules/idtxl/bivariate_pid.html | 151 +-- docs/html/_modules/idtxl/bivariate_te.html | 119 +- docs/html/_modules/idtxl/data.html | 362 +++--- .../embedding_optimization_ais_Rudelt.html | 990 +++++++++------- .../_modules/idtxl/estimators_Rudelt.html | 559 +++++---- docs/html/_modules/idtxl/estimators_jidt.html | 6 +- docs/html/_modules/idtxl/estimators_mpi.html | 118 +- .../idtxl/estimators_multivariate_pid.html | 17 +- .../_modules/idtxl/estimators_opencl.html | 17 +- docs/html/_modules/idtxl/estimators_pid.html | 372 +++--- .../_modules/idtxl/estimators_python.html | 113 +- .../html/_modules/idtxl/idtxl_exceptions.html | 24 +- docs/html/_modules/idtxl/idtxl_io.html | 199 ++-- docs/html/_modules/idtxl/idtxl_utils.html | 55 +- docs/html/_modules/idtxl/multivariate_mi.html | 113 +- .../html/_modules/idtxl/multivariate_pid.html | 138 ++- docs/html/_modules/idtxl/multivariate_te.html | 111 +- .../html/_modules/idtxl/network_analysis.html | 301 +++-- .../_modules/idtxl/network_comparison.html | 496 ++++---- .../_modules/idtxl/network_inference.html | 817 +++++++------ docs/html/_modules/idtxl/postprocessing.html | 518 ++++---- docs/html/_modules/idtxl/results.html | 362 +++--- .../idtxl/single_process_analysis.html | 8 +- docs/html/_modules/idtxl/stats.html | 1047 +++++++++-------- docs/html/_modules/idtxl/visualise_graph.html | 225 ++-- docs/html/_modules/index.html | 6 +- docs/html/_static/documentation_options.js | 2 +- docs/html/genindex.html | 6 +- docs/html/idtxl.html | 93 +- docs/html/idtxl_data_class.html | 14 +- docs/html/idtxl_estimators.html | 81 +- docs/html/idtxl_helper.html | 6 +- 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 | 6 +- docs/html/index.html | 6 +- docs/html/modules.html | 6 +- docs/html/objects.inv | Bin 2759 -> 2761 bytes docs/html/py-modindex.html | 6 +- docs/html/search.html | 6 +- docs/html/searchindex.js | 2 +- 51 files changed, 4503 insertions(+), 3488 deletions(-) diff --git a/docs/doctrees/environment.pickle b/docs/doctrees/environment.pickle index 2570b90f0251d4414cebf79367d79e2b793256b9..9863270cf0dd7068c9d32998da6e6b2f726e3f64 100644 GIT binary patch delta 97093 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 fGmOwTnA&nG@bSwyjA~)pN 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 =7k0`CH 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 z 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�oDUz;|Rt89vnkZ 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 Kz0^A9;r|7UoZ~?N diff --git a/docs/doctrees/idtxl_data_class.doctree b/docs/doctrees/idtxl_data_class.doctree index 99ab30a5176120306dacca100e6149178554007a..46fa3989ebe3c533570252730b45426672170269 100644 GIT binary patch delta 122 zcmX@Nh3mo=E|vz?slpps_;M## z?&QBcl9SK%up^5#|LxiSw}+Ac9AnCM-e{&;X2z81Kw5UXZw!<6^viKf^+18(cqT=l Kd}%z>O)CJGm@72^ delta 118 zcmcbxh3niFE|vz?se&6>_;MLDCzt2>ZBEP;V}vj#C+0?MmdnqVWwf8H=r2C`U5~)z z|2>kE&-bt+M4JEiZ2#ZG$bXKpWIKN}Q!O)N$#ftsJKaBqNqhR$IHr1_KxjOZB2d0O Ip6R9)09gAgpa1{> diff --git a/docs/doctrees/idtxl_estimators.doctree b/docs/doctrees/idtxl_estimators.doctree index 815ec35a2fa3a24c77997b7005d79d89be4f3b0f..49caaf8d645f52a14eb5c5b21e2359b506710122 100644 GIT binary patch delta 19047 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 diff --git a/docs/html/.buildinfo b/docs/html/.buildinfo index f3d5c95e..85f9be7f 100644 --- a/docs/html/.buildinfo +++ b/docs/html/.buildinfo @@ -1,4 +1,4 @@ # Sphinx build info version 1 # This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. -config: ac063bd26c9fc26cbf87edb5d4832784 +config: b08cb61c48070e6f8470e0fc861bc3eb tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/docs/html/_modules/idtxl/active_information_storage.html b/docs/html/_modules/idtxl/active_information_storage.html index f2ed60fe..27dc08d6 100644 --- a/docs/html/_modules/idtxl/active_information_storage.html +++ b/docs/html/_modules/idtxl/active_information_storage.html @@ -5,7 +5,7 @@ - idtxl.active_information_storage — IDTxl 1.5 documentation + idtxl.active_information_storage — IDTxl 1.5.1 documentation @@ -31,7 +31,7 @@

Navigation

  • modules |
  • - + @@ -100,9 +100,13 @@

    Source code for idtxl.active_information_storage

    """ def __init__(self): + self.process = None + self.pvalue = None + self.sign = False + self.ais = None super().__init__() -
    [docs] def analyse_network(self, settings, data, processes='all'): +
    [docs] def analyse_network(self, settings, data, processes="all"): """Estimate active information storage for multiple network processes. Estimate active information storage for all or a subset of processes in @@ -155,44 +159,42 @@

    Source code for idtxl.active_information_storage

    ResultsSingleProcessAnalysis() """ # Set defaults for AIS estimation. - settings.setdefault('verbose', True) - settings.setdefault('fdr_correction', True) + settings.setdefault("verbose", True) + settings.setdefault("fdr_correction", True) # Check provided processes for analysis. - if processes == 'all': - processes = [t for t in range(data.n_processes)] - if (type(processes) is list) and (type(processes[0]) is int): + if processes == "all": + processes = list(range(data.n_processes)) + if isinstance(processes, list) and isinstance(processes[0], int): pass else: - raise ValueError('Processes were not specified correctly: ' - '{0}.'.format(processes)) + raise ValueError(f"Processes were not specified correctly: {processes}.") # Check and set defaults for checkpointing. - self.settings = self._set_checkpointing_defaults( - settings, data, [], processes) + self.settings = self._set_checkpointing_defaults(settings, data, [], processes) # Perform AIS estimation for each target individually. results = ResultsSingleProcessAnalysis( n_nodes=data.n_processes, n_realisations=data.n_realisations(), - normalised=data.normalise) - for t in range(len(processes)): - if settings['verbose']: - print('\n####### analysing process {0} of {1}'.format( - processes[t], processes)) - res_single = self.analyse_single_process( - settings, data, processes[t]) + normalised=data.normalise, + ) + for t, process in enumerate(processes): + if settings["verbose"]: + print(f"\n####### analysing process {process} of {processes}") + res_single = self.analyse_single_process(settings, data, process) results.combine_results(res_single) # Get no. realisations actually used for estimation from single target # analysis. results.data_properties.n_realisations = ( - res_single.data_properties.n_realisations) + res_single.data_properties.n_realisations + ) # Perform FDR-correction on the network level. Add FDR-corrected # results as an extra field. Network_fdr/combine_results internally # creates a deep copy of the results. - if settings['fdr_correction']: + if settings["fdr_correction"]: results = stats.ais_fdr(settings, results) return results
    @@ -266,31 +268,34 @@

    Source code for idtxl.active_information_storage

    self._initialise(settings, data, process) # Main algorithm. - print('\n---------------------------- (1) include candidates') + print("\n---------------------------- (1) include candidates") self._include_process_candidates(data) - print('\n---------------------------- (2) prune source candidates') + print("\n---------------------------- (2) prune source candidates") self._prune_candidates(data) - print('\n---------------------------- (3) final statistics') + print("\n---------------------------- (3) final statistics") self._test_final_conditional(data) # Clean up and return results. - if self.settings['verbose']: - print('final conditional samples: {0}'.format( - self._idx_to_lag(self.selected_vars_full))) + if self.settings["verbose"]: + print( + f"final conditional samples: {self._idx_to_lag(self.selected_vars_full)}" + ) results = ResultsSingleProcessAnalysis( n_nodes=data.n_processes, n_realisations=data.n_realisations(self.current_value), - normalised=data.normalise) + normalised=data.normalise, + ) results._add_single_result( process=self.process, settings=self.settings, results={ - 'current_value': self.current_value, - 'selected_vars': self._idx_to_lag(self.selected_vars_full), - 'ais': self.ais, - 'ais_pval': self.pvalue, - 'ais_sign': self.sign - }) + "current_value": self.current_value, + "selected_vars": self._idx_to_lag(self.selected_vars_full), + "ais": self.ais, + "ais_pval": self.pvalue, + "ais_sign": self.sign, + }, + ) self._reset() # remove realisations and min_stats surrogate table return results
    @@ -298,20 +303,22 @@

    Source code for idtxl.active_information_storage

    """Check input, set initial or default values for analysis settings.""" # Check analysis settings and set defaults. self.settings = settings.copy() - self.settings.setdefault('verbose', True) - self.settings.setdefault('add_conditionals', None) - self.settings.setdefault('tau', 1) - self.settings.setdefault('local_values', False) - - if type(self.settings['max_lag']) is not int or ( - self.settings['max_lag'] < 0): - raise RuntimeError('max_lag has to be an integer >= 0.') - if type(self.settings['tau']) is not int or self.settings['tau'] <= 0: - raise RuntimeError('tau has to be an integer > 0.') - if self.settings['tau'] > self.settings['max_lag']: - raise RuntimeError('tau ({0}) has to be equal to or smaller than max_lag ({1})' - '.'.format(self.settings['tau'], - self.settings['max_lag'])) + self.settings.setdefault("verbose", True) + self.settings.setdefault("add_conditionals", None) + self.settings.setdefault("tau", 1) + self.settings.setdefault("local_values", False) + + if not isinstance(self.settings["max_lag"], int) or ( + self.settings["max_lag"] < 0 + ): + raise RuntimeError("max_lag has to be an integer >= 0.") + if not isinstance(self.settings["tau"], int) or self.settings["tau"] <= 0: + raise RuntimeError("tau has to be an integer > 0.") + if self.settings["tau"] > self.settings["max_lag"]: + raise RuntimeError( + f"tau ({self.settings['tau']}) has to be equal to or smaller than max_lag " + f"({self.settings['max_lag']})." + ) # Set CMI estimator. self._set_cmi_estimator() @@ -320,24 +327,26 @@

    Source code for idtxl.active_information_storage

    self._min_stats_surr_table = None # Check process to be analysed. - if type(process) is not int or process < 0: - raise RuntimeError('The index of the process ({0}) has to be an ' - 'int >= 0.'.format(process)) + if not isinstance(process, int) or process < 0: + raise RuntimeError( + f"The index of the process ({process}) has to be an int >= 0." + ) if process > data.n_processes: - raise RuntimeError('Trying to analyse process with index {0}, ' - 'which greater than the number of processes in ' - 'the data ({1}).'.format(process, - data.n_processes)) + raise RuntimeError( + f"Trying to analyse process with index {process}, which greater than the number " + f"of processes in the data ({data.n_processes})." + ) self.process = process # Check provided search depths for source and target - assert(data.n_samples >= self.settings['max_lag'] + 1), ( - 'Not enough samples in data ({0}) to allow for the chosen maximum ' - 'lag ({1})'.format(data.n_samples, self.settings['max_lag'])) - self.current_value = (process, self.settings['max_lag']) + assert data.n_samples >= self.settings["max_lag"] + 1, ( + f"Not enough samples in data ({data.n_samples}) to allow for the chosen maximum lag " + f"({self.settings['max_lag']})" + ) + self.current_value = (process, self.settings["max_lag"]) [cv_realisation, repl_idx] = data.get_realisations( - current_value=self.current_value, - idx_list=[self.current_value]) + current_value=self.current_value, idx_list=[self.current_value] + ) self._current_value_realisations = cv_realisation # Remember which realisations come from which replication. This may be @@ -350,7 +359,8 @@

    Source code for idtxl.active_information_storage

    # Check and set defaults for checkpointing. self.settings = self._set_checkpointing_defaults( - self.settings, data, [], process) + self.settings, data, [], process + ) # Reset all attributes to inital values if the instance has been used # before. @@ -366,16 +376,17 @@

    Source code for idtxl.active_information_storage

    # Check if the user provided a list of candidates that must go into # the conditioning set. These will be added and used for TE estimation, # but never tested for significance. - if self.settings['add_conditionals'] is not None: - self._force_conditionals(self.settings['add_conditionals'], data) + if self.settings["add_conditionals"] is not None: + self._force_conditionals(self.settings["add_conditionals"], data) def _include_process_candidates(self, data): """Test candidates in the process's past.""" process = [self.process] samples = np.arange( - self.current_value[1] - 1, - self.current_value[1] - self.settings['max_lag'] - 1, - -self.settings['tau']) + self.current_value[1] - 1, + self.current_value[1] - self.settings["max_lag"] - 1, + -self.settings["tau"], + ) candidates = self._define_candidates(process, samples) print(candidates) self._include_candidates(candidates, data) @@ -402,30 +413,31 @@

    Source code for idtxl.active_information_storage

    True if a significant variable was found in the process's past. """ success = False - if self.settings['verbose']: - print('testing candidate set: {0}'.format( - self._idx_to_lag(candidate_set))) + if self.settings["verbose"]: + print(f"testing candidate set: {self._idx_to_lag(candidate_set)}") while candidate_set: # Get realisations for all candidates. - cand_real = data.get_realisations(self.current_value, - candidate_set)[0] + cand_real = data.get_realisations(self.current_value, candidate_set)[0] cand_real = cand_real.T.reshape(cand_real.size, 1) # Calculate the (C)MI for each candidate and the target. try: temp_te = self._cmi_estimator.estimate_parallel( - n_chunks=len(candidate_set), - re_use=['var2', 'conditional'], - var1=cand_real, - var2=self._current_value_realisations, - conditional=self._selected_vars_realisations) + n_chunks=len(candidate_set), + re_use=["var2", "conditional"], + var1=cand_real, + var2=self._current_value_realisations, + conditional=self._selected_vars_realisations, + ) except ex.AlgorithmExhaustedError as aee: # The algorithm cannot continue here, so # we'll terminate the search for more candidates, # though those identified already remain valid - print('AlgorithmExhaustedError encountered in ' - 'estimations: ' + aee.message) - print('Halting current estimation set.') + print( + "AlgorithmExhaustedError encountered in " + "estimations: " + aee.message + ) + print("Halting current estimation set.") # For now we don't need a stack trace: # traceback.print_tb(aee.__traceback__) break @@ -433,21 +445,31 @@

    Source code for idtxl.active_information_storage

    # Test max CMI for significance with maximum statistics. te_max_candidate = max(temp_te) max_candidate = candidate_set[np.argmax(temp_te)] - if self.settings['verbose']: - print('testing candidate {0} '.format( - self._idx_to_lag([max_candidate])[0]), end='') + if self.settings["verbose"]: + print( + "testing candidate {0} ".format( + self._idx_to_lag([max_candidate])[0] + ), + end="", + ) significant = False try: significant = stats.max_statistic( - self, data, candidate_set, te_max_candidate, - conditional=self._selected_vars_realisations)[0] + self, + data, + candidate_set, + te_max_candidate, + conditional=self._selected_vars_realisations, + )[0] except ex.AlgorithmExhaustedError as aee: # The algorithm cannot continue here, so # we'll terminate the check on the max stats and not let the # source pass - print('AlgorithmExhaustedError encountered in ' - 'estimations: ' + aee.message) - print('Halting max stats and further selection for target.') + print( + "AlgorithmExhaustedError encountered in " + "estimations: " + aee.message + ) + print("Halting max stats and further selection for target.") # For now we don't need a stack trace: # traceback.print_tb(aee.__traceback__) break @@ -463,14 +485,14 @@

    Source code for idtxl.active_information_storage

    # selected variables (used as the conditioning set). candidate_set.pop(np.argmax(temp_te)) self._append_selected_vars( - [max_candidate], - data.get_realisations(self.current_value, - [max_candidate])[0]) - if self.settings['write_ckp']: + [max_candidate], + data.get_realisations(self.current_value, [max_candidate])[0], + ) + if self.settings["write_ckp"]: self._write_checkpoint() else: - if self.settings['verbose']: - print(' -- not significant') + if self.settings["verbose"]: + print(" -- not significant") break return success @@ -487,55 +509,66 @@

    Source code for idtxl.active_information_storage

    raw data """ # FOR LATER we don't need to test the last included in the first round - if self.settings['verbose']: + if self.settings["verbose"]: if self.selected_vars_sources: - print('testing candidate set: {0}'.format( - self._idx_to_lag(self.selected_vars_sources)), end='') + print( + "testing candidate set: {0}".format( + self._idx_to_lag(self.selected_vars_sources) + ), + end="", + ) else: - print('no sources selected, nothing to prune ...') + print("no sources selected, nothing to prune ...") while self.selected_vars_sources: # Find the candidate with the minimum TE into the target. cond_dim = len(self.selected_vars_sources) - 1 candidate_realisations = np.empty( - (data.n_realisations(self.current_value) * - len(self.selected_vars_sources), - 1)).astype(data.data_type) + ( + data.n_realisations(self.current_value) + * len(self.selected_vars_sources), + 1, + ) + ).astype(data.data_type) conditional_realisations = np.empty( - (data.n_realisations(self.current_value) * - len(self.selected_vars_sources), - cond_dim)).astype(data.data_type) + ( + data.n_realisations(self.current_value) + * len(self.selected_vars_sources), + cond_dim, + ) + ).astype(data.data_type) i_1 = 0 i_2 = data.n_realisations(self.current_value) for candidate in self.selected_vars_sources: # Separate the candidate realisations and all other # realisations to test the candidate's individual contribution. [temp_cond, temp_cand] = self._separate_realisations( - self.selected_vars_sources, - candidate) + self.selected_vars_sources, candidate + ) if temp_cond is None: conditional_realisations = None - re_use = ['var2', 'conditional'] + re_use = ["var2", "conditional"] else: - conditional_realisations[i_1:i_2, ] = temp_cond - re_use = ['var2'] - candidate_realisations[i_1:i_2, ] = temp_cand + conditional_realisations[i_1:i_2,] = temp_cond + re_use = ["var2"] + candidate_realisations[i_1:i_2,] = temp_cand i_1 = i_2 i_2 += data.n_realisations(self.current_value) try: temp_te = self._cmi_estimator.estimate_parallel( - n_chunks=len(self.selected_vars_sources), - re_use=re_use, - var1=candidate_realisations, - var2=self._current_value_realisations, - conditional=conditional_realisations) + n_chunks=len(self.selected_vars_sources), + re_use=re_use, + var1=candidate_realisations, + var2=self._current_value_realisations, + conditional=conditional_realisations, + ) except ex.AlgorithmExhaustedError as aee: # The algorithm cannot continue here, so we'll terminate the # pruning check, assuming that we need not prune any more - print('AlgorithmExhaustedError encountered in ' - 'estimations: ' + aee.message) - print('Halting current pruning and allowing others to' - ' remain.') + print( + "AlgorithmExhaustedError encountered in estimations: " + aee.message + ) + print("Halting current pruning and allowing others to remain.") # For now we don't need a stack trace: # traceback.print_tb(aee.__traceback__) break @@ -543,28 +576,30 @@

    Source code for idtxl.active_information_storage

    # Test min TE for significance with minimum statistics. te_min_candidate = min(temp_te) min_candidate = self.selected_vars_sources[np.argmin(temp_te)] - if self.settings['verbose']: - print('testing candidate: {0}'.format( - self._idx_to_lag([min_candidate])[0])) + if self.settings["verbose"]: + print(f"testing candidate: {self._idx_to_lag([min_candidate])[0]}") remaining_candidates = set(self.selected_vars_sources).difference( - set([min_candidate])) + set([min_candidate]) + ) conditional_realisations = data.get_realisations( - self.current_value, remaining_candidates)[0] + self.current_value, remaining_candidates + )[0] try: [significant, p, surr_table] = stats.min_statistic( - analysis_setup=self, - data=data, - candidate_set=self.selected_vars_sources, - te_min_candidate=te_min_candidate, - conditional=conditional_realisations) + analysis_setup=self, + data=data, + candidate_set=self.selected_vars_sources, + te_min_candidate=te_min_candidate, + conditional=conditional_realisations, + ) except ex.AlgorithmExhaustedError as aee: # The algorithm cannot continue here, so # we'll terminate the min statistics # assuming that we need not prune any more - print('AlgorithmExhaustedError encountered in ' - 'estimations: ' + aee.message) - print('Halting current pruning and allowing others to' - ' remain.') + print( + "AlgorithmExhaustedError encountered in estimations: " + aee.message + ) + print("Halting current pruning and allowing others to remain.") # For now we don't need a stack trace: # traceback.print_tb(aee.__traceback__) break @@ -576,29 +611,32 @@

    Source code for idtxl.active_information_storage

    # if self.settings['verbose']: # print(' -- not significant') self._remove_selected_var(min_candidate) - if self.settings['write_ckp']: + if self.settings["write_ckp"]: self._write_checkpoint() else: - if self.settings['verbose']: - print(' -- significant') + if self.settings["verbose"]: + print(" -- significant") self._min_stats_surr_table = surr_table break def _test_final_conditional(self, data): """Perform statistical test on AIS using the final conditional set.""" if self._selected_vars_full: - if self.settings['verbose']: - print('selected sources: {0}'.format( - self._idx_to_lag(self.selected_vars_full))) + if self.settings["verbose"]: + print(f"selected sources: {self._idx_to_lag(self.selected_vars_full)}") try: - [ais, s, p] = stats.mi_against_surrogates(self, data) + ais, s, p = stats.mi_against_surrogates(self, data) except ex.AlgorithmExhaustedError as aee: # The algorithm cannot continue here, so # we'll set the results to zero - print('AlgorithmExhaustedError encountered in ' - 'estimations: ' + aee.message) - print('Halting AIS final conditional test and setting to not ' - 'significant.') + print( + "AlgorithmExhaustedError encountered in " + "estimations: " + aee.message + ) + print( + "Halting AIS final conditional test and setting to not " + "significant." + ) # For now we don't need a stack trace: # traceback.print_tb(aee.__traceback__) ais = 0 @@ -607,42 +645,49 @@

    Source code for idtxl.active_information_storage

    # If a parallel estimator was used, an array of AIS estimates is # returned. Make the output uniform for both estimator types. - if type(ais) is np.ndarray: - assert ais.shape[0] == 1, 'AIS result is not a scalar.' + if isinstance(ais, np.ndarray): + assert ais.shape[0] == 1, "AIS result is not a scalar." ais = ais[0] - if self.settings['local_values']: + if self.settings["local_values"]: replication_ind = data.get_realisations( - self.current_value, self._selected_vars_sources)[1] + self.current_value, self._selected_vars_sources + )[1] try: local_ais = self._cmi_estimator_local.estimate( - var1=self._current_value_realisations, - var2=self._selected_vars_realisations, - conditional=None) + var1=self._current_value_realisations, + var2=self._selected_vars_realisations, + conditional=None, + ) except ex.AlgorithmExhaustedError as aee: # The algorithm cannot continue here, so # we'll set the results to zero - print('AlgorithmExhaustedError encountered in ' - 'final local AIS estimations: ' + aee.message) - print('Setting all local results to zero (but leaving' - ' surrogate statistical test results)') + print( + "AlgorithmExhaustedError encountered in " + "final local AIS estimations: " + aee.message + ) + print( + "Setting all local results to zero (but leaving surrogate statistical test results)" + ) # For now we don't need a stack trace: # traceback.print_tb(aee.__traceback__) # Return local AIS values of all zeros: # (length gleaned from line below) local_ais = np.zeros( - (max(replication_ind) + 1)*sum(replication_ind == 0)) + (max(replication_ind) + 1) * sum(replication_ind == 0) + ) # Reshape local AIS to a [replications x samples] matrix. self.ais = local_ais.reshape( - max(replication_ind) + 1, sum(replication_ind == 0)) + max(replication_ind) + 1, sum(replication_ind == 0) + ) else: self.ais = ais self.sign = s self.pvalue = p else: - if self.settings['verbose']: - print('no sources selected') + if self.settings["verbose"]: + print("no sources selected") self.ais = np.nan self.sign = False self.pvalue = 1.0 @@ -651,11 +696,11 @@

    Source code for idtxl.active_information_storage

    """Enforce a given conditioning set.""" if type(cond) is tuple: # easily add single variable cond = [cond] - print('Adding the following variables to the conditioning set: {0}.'. - format(cond)) + print("Adding the following variables to the conditioning set: {cond}.") cond_idx = self._lag_to_idx(cond) self._append_selected_vars( - cond_idx, data.get_realisations(self.current_value, cond_idx)[0]) + cond_idx, data.get_realisations(self.current_value, cond_idx)[0] + ) def _reset(self): """Reset instance after analysis.""" @@ -696,7 +741,7 @@

    Navigation

  • modules |
  • - + diff --git a/docs/html/_modules/idtxl/bivariate_mi.html b/docs/html/_modules/idtxl/bivariate_mi.html index 8441a77e..53a8d8e0 100644 --- a/docs/html/_modules/idtxl/bivariate_mi.html +++ b/docs/html/_modules/idtxl/bivariate_mi.html @@ -5,14 +5,13 @@ - idtxl.bivariate_mi — IDTxl 1.4 documentation - - - + idtxl.bivariate_mi — IDTxl 1.5.1 documentation + + + - @@ -32,7 +31,7 @@

    Navigation

  • modules |
  • - + @@ -109,7 +108,7 @@

    Source code for idtxl.bivariate_mi

         def __init__(self):
             super().__init__()
     
    -
    [docs] def analyse_network(self, settings, data, targets='all', sources='all'): +
    [docs] def analyse_network(self, settings, data, targets="all", sources="all"): """Find bivariate mutual information between all nodes in the network. Estimate bivariate mutual information (MI) between all nodes in the @@ -168,53 +167,56 @@

    Source code for idtxl.bivariate_mi

                     analyse_single_target()
             """
             # Set defaults for network inference.
    -        settings.setdefault('verbose', True)
    -        settings.setdefault('fdr_correction', True)
    +        settings.setdefault("verbose", True)
    +        settings.setdefault("fdr_correction", True)
     
             # Check which targets and sources are requested for analysis.
    -        if targets == 'all':
    -            targets = [t for t in range(data.n_processes)]
    -        if sources == 'all':
    -            sources = ['all' for t in targets]
    -        if (type(sources) is list) and (type(sources[0]) is int):
    +        if targets == "all":
    +            targets = list(range(data.n_processes))
    +        if sources == "all":
    +            sources = ["all" for t in targets]
    +        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 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. If requested, initialise
             # checkpointing.
             self.settings = self._set_checkpointing_defaults(
    -            settings, data, sources, targets)
    +            settings, data, sources, targets
    +        )
     
             # Perform MI estimation for each target individually
    -        results = ResultsNetworkInference(n_nodes=data.n_processes,
    -                                          n_realisations=data.n_realisations(),
    -                                          normalised=data.normalise)
    -        for t in range(len(targets)):
    -            if settings['verbose']:
    -                print('####### analysing target {0} of {1}'.format(t, targets))
    -            res_single = self.analyse_single_target(
    -                settings, data, targets[t], sources[t])
    +        results = ResultsNetworkInference(
    +            n_nodes=data.n_processes,
    +            n_realisations=data.n_realisations(),
    +            normalised=data.normalise,
    +        )
    +        for t, target in enumerate(targets):
    +            if settings["verbose"]:
    +                print(f"####### analysing target {t} of {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
             # analysis.
             results.data_properties.n_realisations = (
    -            res_single.data_properties.n_realisations)
    +            res_single.data_properties.n_realisations
    +        )
     
             # Perform FDR-correction on the network level. Add FDR-corrected
             # results as an extra field. Network_fdr/combine_results internally
             # creates a deep copy of the results.
    -        if settings['fdr_correction']:
    +        if settings["fdr_correction"]:
                 results = network_fdr(settings, results)
             return results
    -
    [docs] def analyse_single_target(self, settings, data, target, sources='all'): +
    [docs] def analyse_single_target(self, settings, data, target, sources="all"): """Find bivariate mutual information between sources and a target. Find bivariate mutual information (MI) between all potential source @@ -325,40 +327,42 @@

    Source code for idtxl.bivariate_mi

             self._initialise(settings, data, sources, target)
     
             # Main algorithm.
    -        print('\n---------------------------- (1) include source candidates')
    +        print("\n---------------------------- (1) include source candidates")
             self._include_source_candidates(data)
    -        print('\n---------------------------- (2) prune candidates')
    +        print("\n---------------------------- (2) prune candidates")
             self._prune_candidates(data)
    -        print('\n---------------------------- (3) final statistics')
    +        print("\n---------------------------- (3) final statistics")
             self._test_final_conditional(data)
     
             # Clean up and return results.
    -        if self.settings['verbose']:
    -            print('final source samples: {0}'.format(
    -                    self._idx_to_lag(self.selected_vars_sources)))
    -            print('final target samples: {0}\n\n'.format(
    -                    self._idx_to_lag(self.selected_vars_target)))
    +        if self.settings["verbose"]:
    +            print(
    +                f"final source samples: {self._idx_to_lag(self.selected_vars_sources)}"
    +            )
    +            print(
    +                f"final target samples: {self._idx_to_lag(self.selected_vars_target)}\n\n"
    +            )
             results = ResultsNetworkInference(
                 n_nodes=data.n_processes,
                 n_realisations=data.n_realisations(self.current_value),
    -            normalised=data.normalise)
    +            normalised=data.normalise,
    +        )
             results._add_single_result(
                 target=self.target,
                 settings=self.settings,
                 results={
    -                'sources_tested': self.source_set,
    -                'current_value': self.current_value,
    -                'selected_vars_sources': self._idx_to_lag(
    -                    self.selected_vars_sources),
    -                'selected_vars_target': self._idx_to_lag(
    -                    self.selected_vars_target),
    -                'selected_sources_pval': self.pvalues_sign_sources,
    -                'selected_sources_mi': self.statistic_sign_sources,
    -                'omnibus_mi': self.statistic_omnibus,
    -                'omnibus_pval': self.pvalue_omnibus,
    -                'omnibus_sign': self.sign_omnibus,
    -                'mi': self.statistic_single_link
    -            })
    +                "sources_tested": self.source_set,
    +                "current_value": self.current_value,
    +                "selected_vars_sources": self._idx_to_lag(self.selected_vars_sources),
    +                "selected_vars_target": self._idx_to_lag(self.selected_vars_target),
    +                "selected_sources_pval": self.pvalues_sign_sources,
    +                "selected_sources_mi": self.statistic_sign_sources,
    +                "omnibus_mi": self.statistic_omnibus,
    +                "omnibus_pval": self.pvalue_omnibus,
    +                "omnibus_sign": self.sign_omnibus,
    +                "mi": self.statistic_single_link,
    +            },
    +        )
     
             self._reset()  # remove attributes
             return results
    @@ -374,7 +378,7 @@

    Source code for idtxl.bivariate_mi

       

    Quick search

    @@ -393,14 +397,14 @@

    Navigation

  • modules |
  • - +
    \ No newline at end of file diff --git a/docs/html/_modules/idtxl/bivariate_pid.html b/docs/html/_modules/idtxl/bivariate_pid.html index 2a06b5e8..7ae3f85a 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 documentation + idtxl.bivariate_pid — IDTxl 1.5.1 documentation @@ -31,7 +31,7 @@

    Navigation

  • modules |
  • - + @@ -87,6 +87,10 @@

    Source code for idtxl.bivariate_pid

         """
     
         def __init__(self):
    +        self._pid_estimator = None
    +        self.sources = None
    +        self.target = None
    +        self.results = None
             super().__init__()
     
     
    [docs] def analyse_network(self, settings, data, targets, sources): @@ -147,32 +151,33 @@

    Source code for idtxl.bivariate_pid

                     ResultsPID()
             """
             # Set defaults for PID estimation.
    -        settings.setdefault('verbose', True)
    -        settings.setdefault('lags_pid', np.array([[1, 1]] * len(targets)))
    +        settings.setdefault("verbose", True)
    +        settings.setdefault("lags_pid", np.array([[1, 1]] * len(targets)))
     
             # Check inputs.
    -        if not len(targets) == len(sources) == len(settings['lags_pid']):
    -            raise RuntimeError('Lists of targets, sources, and lags must have'
    -                               'the same lengths.')
    -        list_of_lags = settings['lags_pid']
    +        if not len(targets) == len(sources) == len(settings["lags_pid"]):
    +            raise RuntimeError(
    +                "Lists of targets, sources, and lags must have" "the same lengths."
    +            )
    +        list_of_lags = settings["lags_pid"]
     
             # Perform PID estimation for each target individually
             results = ResultsPID(
                 n_nodes=data.n_processes,
                 n_realisations=data.n_realisations(),
    -            normalised=data.normalise)
    -        for t in range(len(targets)):
    -            if settings['verbose']:
    -                print('\n####### analysing target with index {0} from list {1}'
    -                      .format(t, targets))
    -            settings['lags_pid'] = list_of_lags[t]
    -            res_single = self.analyse_single_target(
    -                settings, data, targets[t], sources[t])
    +            normalised=data.normalise,
    +        )
    +        for t, target in enumerate(targets):
    +            if settings["verbose"]:
    +                print(f"\n####### analysing target with index {t} from list {targets}")
    +            settings["lags_pid"] = list_of_lags[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
             # analysis.
             results.data_properties.n_realisations = (
    -            res_single.data_properties.n_realisations)
    +            res_single.data_properties.n_realisations
    +        )
             return results
    [docs] def analyse_single_target(self, settings, data, target, sources): @@ -236,61 +241,64 @@

    Source code for idtxl.bivariate_pid

             # Estimate PID and significance.
             self._calculate_pid(data)
     
    -        # Add analyis info.
    +        # Add analysis info.
             results = ResultsPID(
                 n_nodes=data.n_processes,
                 n_realisations=data.n_realisations(self.current_value),
    -            normalised=data.normalise)
    +            normalised=data.normalise,
    +        )
             results._add_single_result(
    -            settings=self.settings,
    -            target=self.target,
    -            results=self.results)
    +            settings=self.settings, target=self.target, results=self.results
    +        )
             self._reset()
             return results
    def _initialise(self, settings, data, target, sources): """Check input, set initial or default values for analysis settings.""" # Check requested PID estimator. - assert 'pid_estimator' in settings, 'Estimator was not specified!' - self._pid_estimator = get_estimator(settings['pid_estimator'], settings) + assert "pid_estimator" in settings, "Estimator was not specified!" + self._pid_estimator = get_estimator(settings["pid_estimator"], settings) self.settings = settings.copy() - self.settings.setdefault('lags_pid', [1, 1]) - self.settings.setdefault('verbose', True) + self.settings.setdefault("lags_pid", [1, 1]) + self.settings.setdefault("verbose", True) # Check if provided lags are correct and work with the number of # samples in the data. - if len(self.settings['lags_pid']) != 2: - raise RuntimeError('List of lags must have length 2.') - if self.settings['lags_pid'][0] >= data.n_samples: + if len(self.settings["lags_pid"]) != 2: + raise RuntimeError("List of lags must have length 2.") + if self.settings["lags_pid"][0] >= data.n_samples: raise RuntimeError( - 'Lag 1 ({0}) is larger than the number of samples in the data ' - 'set ({1}).'.format( - self.settings['lags_pid'][0], data.n_samples)) - if self.settings['lags_pid'][1] >= data.n_samples: + f"Lag 1 ({self.settings['lags_pid'][0]}) is larger than the number of " + f"samples in the data set ({data.n_samples})." + ) + if self.settings["lags_pid"][1] >= data.n_samples: raise RuntimeError( - 'Lag 2 ({0}) is larger than the number of samples in the data ' - 'set ({1}).'.format( - self.settings['lags_pid'][1], data.n_samples)) + f"Lag 2 ({self.settings['lags_pid'][1]}) is larger than the number of " + "samples in the data set ({data.n_samples})." + ) # Check if target and sources are provided correctly. - if type(target) is not int: - raise RuntimeError('Target must be an integer.') + if not isinstance(target, int): + raise RuntimeError("Target must be an integer.") if len(sources) != 2: - raise RuntimeError('List of sources must have length 2.') + raise RuntimeError("List of sources must have length 2.") if target in sources: - raise RuntimeError('The target ({0}) should not be in the list ' - 'of sources ({1}).'.format(target, sources)) + raise RuntimeError( + f"The target ({target}) should not be in the list of sources ({sources})." + ) - self.current_value = (target, max(self.settings['lags_pid'])) + self.current_value = (target, max(self.settings["lags_pid"])) self.target = target # TODO works for single vars only, change to multivariate? - self.sources = self._lag_to_idx([ - (sources[0], self.settings['lags_pid'][0]), - (sources[1], self.settings['lags_pid'][1])]) + self.sources = self._lag_to_idx( + [ + (sources[0], self.settings["lags_pid"][0]), + (sources[1], self.settings["lags_pid"][1]), + ] + ) def _calculate_pid(self, data): - # TODO Discuss how and if the following statistical testing should be # included included. Remove dummy results. # [orig_pid, sign_1, p_val_1, @@ -302,32 +310,33 @@

    Source code for idtxl.bivariate_pid

             # p_val_1 = p_val_2 = p_val_shd = p_val_syn = 1.0
     
             target_realisations = data.get_realisations(
    -                                            self.current_value,
    -                                            [self.current_value])[0]
    +            self.current_value, [self.current_value]
    +        )[0]
             source_1_realisations = data.get_realisations(
    -                                            self.current_value,
    -                                            [self.sources[0]])[0]
    +            self.current_value, [self.sources[0]]
    +        )[0]
             source_2_realisations = data.get_realisations(
    -                                            self.current_value,
    -                                             [self.sources[1]])[0]
    +            self.current_value, [self.sources[1]]
    +        )[0]
             orig_pid = self._pid_estimator.estimate(
    -                                s1=source_1_realisations,
    -                                s2=source_2_realisations,
    -                                t=target_realisations)
    -
    -        if self.settings['verbose']:
    -            print('\nunq information s1: {0:.8f}, s2: {1:.8f}'.format(
    -                                                           orig_pid['unq_s1'],
    -                                                           orig_pid['unq_s2']))
    -            print('shd information: {0:.8f}, syn information: {1:.8f}'.format(
    -                                                        orig_pid['shd_s1_s2'],
    -                                                        orig_pid['syn_s1_s2']))
    +            s1=source_1_realisations, s2=source_2_realisations, t=target_realisations
    +        )
    +
    +        if self.settings["verbose"]:
    +            print(
    +                f"\nunq information s1: {orig_pid['unq_s1']:.8f}, s2: {orig_pid['unq_s2']:.8f}"
    +            )
    +            print(
    +                f"shd information: {orig_pid['shd_s1_s2']:.8f}, syn information: {orig_pid['syn_s1_s2']:.8f}"
    +            )
             self.results = orig_pid
    -        self.results['source_1'] = self._idx_to_lag([self.sources[0]])
    -        self.results['source_2'] = self._idx_to_lag([self.sources[1]])
    -        self.results['selected_vars_sources'] = [
    -            self.results['source_1'][0], self.results['source_2'][0]]
    -        self.results['current_value'] = self.current_value
    +        self.results["source_1"] = self._idx_to_lag([self.sources[0]])
    +        self.results["source_2"] = self._idx_to_lag([self.sources[1]])
    +        self.results["selected_vars_sources"] = [
    +            self.results["source_1"][0],
    +            self.results["source_2"][0],
    +        ]
    +        self.results["current_value"] = self.current_value
             # self.results['unq_s1_sign'] = sign_1
             # self.results['unq_s2_sign'] = sign_2
             # self.results['unq_s1_p_val'] = p_val_1
    @@ -338,8 +347,8 @@ 

    Source code for idtxl.bivariate_pid

             # self.results['shd_p_val'] = p_val_shd
     
             # TODO make mi_against_surrogates in stats more generic, such that
    -        # it becomes an arbitrary permutation test where one arguemnt gets
    -        # shuffled and then all arguents are passed to the provided estimator
    +        # it becomes an arbitrary permutation test where one argument gets
    +        # shuffled and then all arguments are passed to the provided estimator
     
         def _reset(self):
             """Reset instance after analysis."""
    @@ -378,7 +387,7 @@ 

    Navigation

  • modules |
  • - + diff --git a/docs/html/_modules/idtxl/bivariate_te.html b/docs/html/_modules/idtxl/bivariate_te.html index e27b65be..fb12e492 100644 --- a/docs/html/_modules/idtxl/bivariate_te.html +++ b/docs/html/_modules/idtxl/bivariate_te.html @@ -5,14 +5,13 @@ - idtxl.bivariate_te — IDTxl 1.4 documentation - - - + idtxl.bivariate_te — IDTxl 1.5.1 documentation + + + - @@ -32,7 +31,7 @@

    Navigation

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

    Source code for idtxl.bivariate_te

         def __init__(self):
             super().__init__()
     
    -
    [docs] def analyse_network(self, settings, data, targets='all', sources='all'): +
    [docs] def analyse_network(self, settings, data, targets="all", sources="all"): """Find bivariate transfer entropy between all nodes in the network. Estimate bivariate transfer entropy (TE) between all nodes in the @@ -171,54 +170,56 @@

    Source code for idtxl.bivariate_te

                     ResultsNetworkInference()
             """
             # Set defaults for network inference.
    -        settings.setdefault('verbose', True)
    -        settings.setdefault('fdr_correction', True)
    +        settings.setdefault("verbose", True)
    +        settings.setdefault("fdr_correction", True)
     
             # Check which targets and sources are requested for analysis.
    -        if targets == 'all':
    -            targets = [t for t in range(data.n_processes)]
    -        if sources == 'all':
    -            sources = ['all' for t in targets]
    -        if (type(sources) is list) and (type(sources[0]) is int):
    +        if targets == "all":
    +            targets = list(range(data.n_processes))
    +        if sources == "all":
    +            sources = ["all" for t in targets]
    +        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 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 length"
     
             # Check and set defaults for checkpointing. If requested, initialise
             # checkpointing.
             self.settings = self._set_checkpointing_defaults(
    -            settings, data, sources, targets)
    +            settings, data, sources, targets
    +        )
     
             # Perform TE estimation for each target individually
    -        results = ResultsNetworkInference(n_nodes=data.n_processes,
    -                                          n_realisations=data.n_realisations(),
    -                                          normalised=data.normalise)
    -        for t in range(len(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])
    +        results = ResultsNetworkInference(
    +            n_nodes=data.n_processes,
    +            n_realisations=data.n_realisations(),
    +            normalised=data.normalise,
    +        )
    +        for t, target in enumerate(targets):
    +            if settings["verbose"]:
    +                print("\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
             # analysis.
             results.data_properties.n_realisations = (
    -            res_single.data_properties.n_realisations)
    +            res_single.data_properties.n_realisations
    +        )
     
             # Perform FDR-correction on the network level. Add FDR-corrected
             # results as an extra field. Network_fdr/combine_results internally
             # creates a deep copy of the results.
    -        if settings['fdr_correction']:
    +        if settings["fdr_correction"]:
                 results = network_fdr(settings, results)
             return results
    -
    [docs] def analyse_single_target(self, settings, data, target, sources='all'): +
    [docs] def analyse_single_target(self, settings, data, target, sources="all"): """Find bivariate transfer entropy between sources and a target. Find bivariate transfer entropy (TE) between all potential source @@ -328,42 +329,44 @@

    Source code for idtxl.bivariate_te

             self._initialise(settings, data, sources, target)
     
             # Main algorithm.
    -        print('\n---------------------------- (1) include target candidates')
    +        print("\n---------------------------- (1) include target candidates")
             self._include_target_candidates(data)
    -        print('\n---------------------------- (2) include source candidates')
    +        print("\n---------------------------- (2) include source candidates")
             self._include_source_candidates(data)
    -        print('\n---------------------------- (3) prune candidates')
    +        print("\n---------------------------- (3) prune candidates")
             self._prune_candidates(data)
    -        print('\n---------------------------- (4) final statistics')
    +        print("\n---------------------------- (4) final statistics")
             self._test_final_conditional(data)
     
             # Clean up and return results.
    -        if self.settings['verbose']:
    -            print('final source samples: {0}'.format(
    -                    self._idx_to_lag(self.selected_vars_sources)))
    -            print('final target samples: {0}\n\n'.format(
    -                    self._idx_to_lag(self.selected_vars_target)))
    +        if self.settings["verbose"]:
    +            print(
    +                f"final source samples: {self._idx_to_lag(self.selected_vars_sources)}"
    +            )
    +            print(
    +                f"final target samples: {self._idx_to_lag(self.selected_vars_target)}\n\n"
    +            )
             results = ResultsNetworkInference(
                 n_nodes=data.n_processes,
                 n_realisations=data.n_realisations(self.current_value),
    -            normalised=data.normalise)
    +            normalised=data.normalise,
    +        )
             results._add_single_result(
                 target=self.target,
                 settings=self.settings,
                 results={
    -                'sources_tested': self.source_set,
    -                'current_value': self.current_value,
    -                'selected_vars_sources': self._idx_to_lag(
    -                    self.selected_vars_sources),
    -                'selected_vars_target': self._idx_to_lag(
    -                    self.selected_vars_target),
    -                'selected_sources_pval': self.pvalues_sign_sources,
    -                'selected_sources_te': self.statistic_sign_sources,
    -                'omnibus_te': self.statistic_omnibus,
    -                'omnibus_pval': self.pvalue_omnibus,
    -                'omnibus_sign': self.sign_omnibus,
    -                'te': self.statistic_single_link
    -            })
    +                "sources_tested": self.source_set,
    +                "current_value": self.current_value,
    +                "selected_vars_sources": self._idx_to_lag(self.selected_vars_sources),
    +                "selected_vars_target": self._idx_to_lag(self.selected_vars_target),
    +                "selected_sources_pval": self.pvalues_sign_sources,
    +                "selected_sources_te": self.statistic_sign_sources,
    +                "omnibus_te": self.statistic_omnibus,
    +                "omnibus_pval": self.pvalue_omnibus,
    +                "omnibus_sign": self.sign_omnibus,
    +                "te": self.statistic_single_link,
    +            },
    +        )
             self._reset()  # remove attributes
             return results
    @@ -378,7 +381,7 @@

    Source code for idtxl.bivariate_te

       

    Quick search

    @@ -397,14 +400,14 @@

    Navigation

  • modules |
  • - +
    \ No newline at end of file diff --git a/docs/html/_modules/idtxl/data.html b/docs/html/_modules/idtxl/data.html index 30eb98f1..ecb19811 100644 --- a/docs/html/_modules/idtxl/data.html +++ b/docs/html/_modules/idtxl/data.html @@ -5,14 +5,13 @@ - idtxl.data — IDTxl 1.4 documentation - - - + idtxl.data — IDTxl 1.5.1 documentation + + + - @@ -32,7 +31,7 @@

    Navigation

  • modules |
  • - + @@ -51,7 +50,7 @@

    Source code for idtxl.data

     VERBOSE = False
     
     
    -
    [docs]class Data(): +
    [docs]class Data: """Store data for information dynamics estimation. Data takes a 1- to 3-dimensional array representing realisations of random @@ -114,10 +113,13 @@

    Source code for idtxl.data

                 initial state of the seed for shuffled permutations
         """
     
    -    def __init__(self, data=None, dim_order='psr', normalise=True, seed=None):
    +    def __init__(self, data=None, dim_order="psr", normalise=True, seed=None):
             np.random.seed(seed)
             self.initial_state = np.random.get_state()
             self.normalise = normalise
    +        self.n_processes = 0
    +        self.n_samples = 0
    +        self.n_replications = 0
             if data is not None:
                 self.set_data(data, dim_order)
     
    @@ -132,8 +134,7 @@ 

    Source code for idtxl.data

                     samples to the embedding); if no current_value is provided, the
                     number of all samples is used
             """
    -        return (self.n_realisations_samples(current_value) *
    -                self.n_realisations_repl())
    + return self.n_realisations_samples(current_value) * self.n_realisations_repl()
    [docs] def n_realisations_samples(self, current_value=None): """Number of realisations over samples. @@ -147,13 +148,12 @@

    Source code for idtxl.data

             """
             if current_value is None:
                 return self.n_samples
    -        else:
    -            if current_value[1] >= self.n_samples:
    -                raise RuntimeError('The sample index of the current value '
    -                                   '({0}) is larger than the number of samples'
    -                                   ' in the data set ({1}).'.format(
    -                                              current_value, self.n_samples))
    -            return self.n_samples - current_value[1]
    + if current_value[1] >= self.n_samples: + raise RuntimeError( + f"The sample index of the current value ({current_value}) is larger than the" + f" number of samples in the data set ({self.n_samples})." + ) + return self.n_samples - current_value[1]
    [docs] def n_realisations_repl(self): """Number of realisations over replications.""" @@ -166,16 +166,17 @@

    Source code for idtxl.data

     
         @data.setter
         def data(self, d):
    -        if hasattr(self, 'data'):
    -            raise AttributeError('You can not assign a value to this attribute'
    -                                 ' directly, use the set_data method instead.')
    -        else:
    -            self._data = d
    +        if hasattr(self, "data"):
    +            raise AttributeError(
    +                "You can not assign a value to this attribute"
    +                " directly, use the set_data method instead."
    +            )
    +        self._data = d
     
         @data.deleter
         def data(self):
    -        print('overwriting existing data')
    -        del(self._data)
    +        print("overwriting existing data")
    +        del self._data
     
     
    [docs] def set_data(self, data, dim_order): """Overwrite data in an existing Data object. @@ -189,22 +190,23 @@

    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('Data array dimension ({0}) and length of '
    -                               'dim_order ({1}) are not equal.'.format(
    -                                           data.ndim, len(dim_order)))
    +            raise RuntimeError(
    +                f"Data array dimension ({data.ndim}) and length of "
    +                f"dim_order ({len(dim_order)}) are not equal."
    +            )
     
             # Bring data into the order processes x samples x replications and set
             # set 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))
    +        print(
    +            "Adding data with properties: {0} processes, {1} samples, {2} "
    +            "replications".format(self.n_processes, self.n_samples, self.n_replications)
    +        )
             try:
    -            delattr(self, 'data')
    +            delattr(self, "data")
             except AttributeError:
                 pass
             if self.normalise:
    @@ -218,29 +220,30 @@ 

    Source code for idtxl.data

             d_standardised = np.empty(d.shape)
             for process in range(self.n_processes):
                 s = utils.standardise(
    -                            d[process, :, :].reshape(1, self.n_realisations()),
    -                            dimension=1)
    -            d_standardised[process, :, :] = s.reshape(self.n_samples,
    -                                                      self.n_replications)
    +                d[process, :, :].reshape(1, self.n_realisations()), dimension=1
    +            )
    +            d_standardised[process, :, :] = s.reshape(
    +                self.n_samples, self.n_replications
    +            )
             return d_standardised
     
         def _reorder_data(self, data, dim_order):
             """Reorder data dimensions to processes x samples x replications."""
             # add singletons for missing dimensions
    -        missing_dims = 'psr'
    +        missing_dims = "psr"
             for dim in dim_order:
    -            missing_dims = missing_dims.replace(dim, '')
    +            missing_dims = missing_dims.replace(dim, "")
             for dim in missing_dims:
                 data = np.expand_dims(data, data.ndim)
                 dim_order += dim
     
             # reorder array dims if necessary
    -        if dim_order[0] != 'p':
    -            ind_p = dim_order.index('p')
    +        if dim_order[0] != "p":
    +            ind_p = dim_order.index("p")
                 data = data.swapaxes(0, ind_p)
                 dim_order = utils.swap_chars(dim_order, 0, ind_p)
    -        if dim_order[1] != 's':
    -            data = data.swapaxes(1, dim_order.index('s'))
    +        if dim_order[1] != "s":
    +            data = data.swapaxes(1, dim_order.index("s"))
             return data
     
         def _set_data_size(self, data):
    @@ -297,24 +300,24 @@ 

    Source code for idtxl.data

                     replication index for each realisation with dimensions (no.
                     samples * no.replications) x number of indices
             """
    -        if not hasattr(self, 'data'):
    -            raise AttributeError('No data has been added to this Data() '
    -                                 'instance.')
    +        if not hasattr(self, "data"):
    +            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
             # Check if requested indices are smaller than the current_value.
             if not all(np.array([x[1] for x in idx_list]) <= current_value[1]):
    -            print('Index list: {0}\ncurrent value: {1}'.format(idx_list,
    -                                                               current_value))
    -            raise RuntimeError('All indices for which data is retrieved must '
    -                               ' be smaller than the current value.')
    +            print(f"Index list: {idx_list}\ncurrent value: {current_value}")
    +            raise RuntimeError(
    +                "All indices for which data is retrieved must be smaller than the current value."
    +            )
     
             # Allocate memory.
             n_real_time = self.n_realisations_samples(current_value)
             n_real_repl = self.n_realisations_repl()
    -        realisations = np.empty((n_real_time * n_real_repl,
    -                                 len(idx_list))).astype(self.data_type)
    +        realisations = np.empty((n_real_time * n_real_repl, len(idx_list))).astype(
    +            self.data_type
    +        )
     
             # Shuffle the replication order if requested. This creates surrogate
             # data by permuting replications while keeping the order of samples
    @@ -331,30 +334,31 @@ 

    Source code for idtxl.data

                 # get the last sample as negative value, i.e., no. samples from the
                 # end of the array
                 last_sample = idx[1] - current_value[1]  # indexing is much faster
    -            if last_sample == 0:                     # than looping over time!
    +            if last_sample == 0:  # than looping over time!
                     last_sample = None
                 for replication in replications_order:
                     try:
    -                    realisations[r:r + n_real_time, i] = self.data[
    -                                                        idx[0],
    -                                                        idx[1]: last_sample,
    -                                                        replication]
    +                    realisations[r : r + n_real_time, i] = self.data[
    +                        idx[0], idx[1] : last_sample, replication
    +                    ]
                     except IndexError:
    -                    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))
    +                    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)
    +                    )
                     r += n_real_time
     
    -            assert(not np.isnan(realisations[:, i]).any()), ('There are nans '
    -                                                             'in the retrieved'
    -                                                             ' realisations.')
    +            assert not np.isnan(
    +                realisations[:, i]
    +            ).any(), "There are nans in the retrieved realisations."
                 i += 1
     
             # For each realisation keep the index of the replication it came from.
             replications_index = np.repeat(replications_order, n_real_time)
    -        assert(replications_index.shape[0] == realisations.shape[0]), (
    -               'There seems to be a problem with the replications index.')
    +        assert (
    +            replications_index.shape[0] == realisations.shape[0]
    +        ), "There seems to be a problem with the replications index."
     
             return realisations, replications_index
    @@ -362,7 +366,7 @@

    Source code for idtxl.data

             """Return data slice for a single process.
     
             Return data slice for process. Optionally, an offset can be provided
    -        such that data are returnded from sample 'offset_samples' onwards.
    +        such that data are returned from sample 'offset_samples' onwards.
             Also, data
             can be shuffled over replications to create surrogate data for
             statistical testing. For shuffling, data blocks are permuted over
    @@ -402,10 +406,12 @@ 

    Source code for idtxl.data

                     list of replications indices
             """
             # Check if requested indices are smaller than the current_value.
    -        if not offset_samples <= self.n_samples:
    -            print('Offset {0} must be smaller than number of samples in the '
    -                  ' data ({1})'.format(offset_samples, self.n_samples))
    -            raise RuntimeError('Offset must be smaller than no. samples.')
    +        if offset_samples > self.n_samples:
    +            print(
    +                "Offset {0} must be smaller than number of samples in the "
    +                " data ({1})".format(offset_samples, self.n_samples)
    +            )
    +            raise RuntimeError("Offset must be smaller than no. samples.")
     
             # Shuffle the replication order if requested. This creates surrogate
             # data by permuting replications while keeping the order of samples
    @@ -418,13 +424,16 @@ 

    Source code for idtxl.data

             try:
                 data_slice = self.data[process, offset_samples:, replication_index]
             except IndexError:
    -            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))
    -        assert(not np.isnan(data_slice).any()), ('There are nans in the '
    -                                                 'retrieved data slice.')
    +            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
    +                )
    +            )
    +        assert not np.isnan(
    +            data_slice
    +        ).any(), "There are nans in the retrieved data slice."
             return data_slice.T, replication_index
     
     
    [docs] def slice_permute_replications(self, process): @@ -526,7 +535,7 @@

    Source code for idtxl.data

                     data slice with data permuted over samples with dimensions
                     samples x number of replications
                 numpy array
    -                index of permutet samples
    +                index of permuted samples
     
             Note:
                 This permutation scheme is the fall-back option if the number of
    @@ -535,8 +544,7 @@ 

    Source code for idtxl.data

             """
             data_slice = self._get_data_slice(process, shuffle=True)[0]
             data_slice_perm = np.empty(data_slice.shape).astype(self.data_type)
    -        perm = self._get_permutation_samples(data_slice.shape[0],
    -                                             perm_settings)
    +        perm = self._get_permutation_samples(data_slice.shape[0], perm_settings)
             for r in range(self.n_replications):
                 data_slice_perm[:, r] = data_slice[perm, r]
             return data_slice_perm, perm
    @@ -581,8 +589,8 @@

    Source code for idtxl.data

             Raises:
                 TypeError if idx_realisations is not a list
             """
    -        if type(idx_list) is not list:
    -            raise TypeError('idx needs to be a list of tuples.')
    +        if not isinstance(idx_list, list):
    +            raise TypeError("idx needs to be a list of tuples.")
             return self.get_realisations(current_value, idx_list, shuffle=True)
    [docs] def permute_samples(self, current_value, idx_list, perm_settings): @@ -697,8 +705,7 @@

    Source code for idtxl.data

                 replications is too small to generate the requested number of
                 permutations.
             """
    -        [realisations, replication_idx] = self.get_realisations(current_value,
    -                                                                idx_list)
    +        [realisations, replication_idx] = self.get_realisations(current_value, idx_list)
             n_samples = sum(replication_idx == 0)
             perm = self._get_permutation_samples(n_samples, perm_settings)
             # Apply the permutation to data from each replication.
    @@ -734,37 +741,36 @@ 

    Source code for idtxl.data

                 numpy Array
                     permuted indices of samples
             """
    -        perm_type = perm_settings['perm_type']
    +        perm_type = perm_settings["perm_type"]
     
    -        # Get the permutaion 'mask' for one replication (the same mask is then
    +        # Get the permutation 'mask' for one replication (the same mask is then
             # applied to each replication).
    -        if perm_type == 'random':
    +        if perm_type == "random":
                 perm = np.random.permutation(n_samples)
     
    -        elif perm_type == 'circular':
    -            max_shift = perm_settings['max_shift']
    -            if type(max_shift) is not int or max_shift < 1:
    -                raise TypeError(' ''max_shift'' has to be an int > 0.')
    +        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.")
                 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 type(block_size) is not int or block_size < 1:
    -                raise TypeError(' ''block_size'' has to be an int > 0.')
    -            if type(perm_range) is not int or perm_range < 1:
    -                raise TypeError(' ''perm_range'' has to be an int > 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.")
    +            if not isinstance(perm_range, int) or perm_range < 1:
    +                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 type(perm_range) is not int or perm_range < 1:
    -                raise TypeError(' ''perm_range'' has to be an int > 0.')
    +        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.")
                 perm = self._swap_local(n_samples, perm_range)
     
             else:
    -            raise ValueError('Unknown permutation type ({0}).'.format(
    -                                                                    perm_type))
    +            raise ValueError(f"Unknown permutation type ({perm_type}).")
             return perm
     
         def _swap_local(self, n, perm_range):
    @@ -780,12 +786,14 @@ 

    Source code for idtxl.data

                 numpy array
                     permuted indices with length n
             """
    -        assert (perm_range > 1), ('Permutation range has to be larger than 1',
    -                                  'otherwise there is nothing to permute.')
    -        assert (n >= perm_range), ('Not enough realisations per replication '
    -                                   '({0}) to allow for the requested '
    -                                   '"perm_range" of {1}.' .format(n,
    -                                                                  perm_range))
    +        assert perm_range > 1, (
    +            "Permutation range has to be larger than 1",
    +            "otherwise there is nothing to permute.",
    +        )
    +        assert n >= perm_range, (
    +            f"Not enough realisations per replication ({n}) to allow for the requested "
    +            f"'perm_range' of {perm_range}."
    +        )
     
             if perm_range == n:  # permute all n samples randomly
                 perm = np.random.permutation(n)
    @@ -794,7 +802,7 @@ 

    Source code for idtxl.data

                 remainder = n % perm_range
                 i = 0
                 for p in range(n // perm_range):
    -                perm[i:i + perm_range] = np.random.permutation(perm_range) + i
    +                perm[i : i + perm_range] = np.random.permutation(perm_range) + i
                     i += perm_range
                 if remainder > 0:
                     perm[-remainder:] = np.random.permutation(remainder) + i
    @@ -831,17 +839,19 @@ 

    Source code for idtxl.data

     
                 i = 0
                 for p in range(n_blocks // perm_range):
    -                perm_blocks[i:i + perm_range] = np.random.permutation(
    -                                                                perm_range) + i
    +                perm_blocks[i : i + perm_range] = np.random.permutation(perm_range) + i
                     i += perm_range
                 if rem_blocks > 0:
    -                perm_blocks[-rem_blocks:] = np.random.permutation(
    -                                                                rem_blocks) + i
    +                perm_blocks[-rem_blocks:] = np.random.permutation(rem_blocks) + i
     
             # Get the block index for each sample index, take into account that the
             # last block may have fewer samples if n_samples % block_size isn't 0.
    -        idx_blocks = np.hstack((np.repeat(np.arange(n_blocks - 1), block_size),
    -                                np.repeat(n_blocks - 1, rem_samples)))
    +        idx_blocks = np.hstack(
    +            (
    +                np.repeat(np.arange(n_blocks - 1), block_size),
    +                np.repeat(n_blocks - 1, rem_samples),
    +            )
    +        )
     
             # Permute samples indices according to permuted block indices.
             perm = np.zeros(n).astype(int)  # allocate memory
    @@ -875,22 +885,22 @@ 

    Source code for idtxl.data

                 int
                     no. samples by which the time series was shifted
             """
    -        assert (max_shift <= n), ('Max_shift ({0}) has to be equal to or '
    -                                  'smaller than the number of samples in the '
    -                                  'time series ({1}).'.format(max_shift, n))
    +        assert max_shift <= n, (
    +            f"Max_shift ({max_shift}) has to be equal to or smaller than the number of samples in the "
    +            f"time series ({n})."
    +        )
             shift = np.random.randint(low=1, high=max_shift + 1)
             if VERBOSE:
    -            print("replications are shifted by {0} samples".format(shift))
    -        return np.hstack((np.arange(n - shift, n),
    -                          np.arange(n - shift))), shift
    +            print(f"replications are shifted by {shift} samples")
    +        return np.hstack((np.arange(n - shift, n), np.arange(n - shift))), shift
     
     
    [docs] def generate_mute_data(self, n_samples=1000, n_replications=10): """Generate example data for a 5-process network. Generate example data and overwrite the instance's current data. The network is used as an example the paper on the MuTE toolbox (Montalto, - PLOS ONE, 2014, eq. 14) and was orginially proposed by Baccala & - Sameshima (2001). The network consists of five autoregressive (AR) + PLOS ONE, 2014, eq. 14) and was orginally proposed by Baccala & + Sameshima (2001). The network consists of five auto-regressive (AR) processes with model orders 2 and the following (non-linear) couplings: 0 -> 1, u = 2 (non-linear) @@ -916,39 +926,42 @@

    Source code for idtxl.data

                     number of replications
             """
             n_processes = 5
    -        n_samples = n_samples
    -        n_replications = n_replications
     
    -        x = np.zeros((n_processes, n_samples + 3,
    -                      n_replications))
    -        x[:, 0:3, :] = np.random.normal(size=(n_processes, 3,
    -                                              n_replications))
    +        x = np.zeros((n_processes, n_samples + 3, n_replications))
    +        x[:, 0:3, :] = np.random.normal(size=(n_processes, 3, n_replications))
             term_1 = 0.95 * np.sqrt(2)
             term_2 = 0.25 * np.sqrt(2)
             term_3 = -0.25 * np.sqrt(2)
             for r in range(n_replications):
                 for n in range(3, n_samples + 3):
    -                x[0, n, r] = (term_1 * x[0, n - 1, r] -
    -                              0.9025 * x[0, n - 2, r] + np.random.normal())
    +                x[0, n, r] = (
    +                    term_1 * x[0, n - 1, r]
    +                    - 0.9025 * x[0, n - 2, r]
    +                    + np.random.normal()
    +                )
                     x[1, n, r] = 0.5 * x[0, n - 2, r] ** 2 + np.random.normal()
                     x[2, n, r] = -0.4 * x[0, n - 3, r] + np.random.normal()
    -                x[3, n, r] = (-0.5 * x[0, n - 2, r] ** 2 +
    -                              term_2 * x[3, n - 1, r] +
    -                              term_2 * x[4, n - 1, r] +
    -                              np.random.normal())
    -                x[4, n, r] = (term_3 * x[3, n - 1, r] +
    -                              term_2 * x[4, n - 1, r] +
    -                              np.random.normal())
    -        self.set_data(x[:, 3:, :], 'psr')
    + x[3, n, r] = ( + -0.5 * x[0, n - 2, r] ** 2 + + term_2 * x[3, n - 1, r] + + term_2 * x[4, n - 1, r] + + np.random.normal() + ) + x[4, n, r] = ( + term_3 * x[3, n - 1, r] + + term_2 * x[4, n - 1, r] + + np.random.normal() + ) + self.set_data(x[:, 3:, :], "psr")
    [docs] def generate_var_data( self, n_samples=1000, n_replications=10, coefficient_matrices=np.array([[[0.5, 0], [0.4, 0.5]]]), - noise_std=0.1 + noise_std=0.1, ): - """Generate discrete-time VAR (vector autoregressive) time series. + """Generate discrete-time VAR (vector auto-regressive) time series. Generate data and overwrite the instance's current data. @@ -974,39 +987,33 @@

    Source code for idtxl.data

     
             # Check stability of the VAR process, which is a sufficient condition
             # for stationarity.
    -        var_reduced_form = np.zeros((
    -            n_processes * order,
    -            n_processes * order
    -        ))
    +        var_reduced_form = np.zeros((n_processes * order, n_processes * order))
             var_reduced_form[0:n_processes, :] = np.reshape(
                 np.transpose(coefficient_matrices, (1, 0, 2)),
    -            [n_processes, n_processes * order]
    -            )
    -        var_reduced_form[n_processes:, 0:n_processes * (order - 1)] = np.eye(
    +            [n_processes, n_processes * order],
    +        )
    +        var_reduced_form[n_processes:, 0 : n_processes * (order - 1)] = np.eye(
                 n_processes * (order - 1)
             )
             # Condition for stability: the absolute values of all the eigenvalues
    -        # of the reduced-form coefficeint matrix are smaller than 1. A stable
    +        # of the reduced-form coefficient matrix are smaller than 1. A stable
             # VAR process is also stationary.
             is_stable = max(np.abs(np.linalg.eigvals(var_reduced_form))) < 1
             if not is_stable:
    -            RuntimeError('VAR process is not stable and may be nonstationary.')
    +            raise RuntimeError("VAR process is not stable and may be nonstationary.")
     
             # Initialise time series matrix. The 3 dimensions represent
             # (processes, samples, replications). Only the last n_samples will be
             # kept, in order to discard transient effects.
    -        x = np.zeros((
    -            n_processes,
    -            order + samples_transient + n_samples,
    -            n_replications
    -        ))
    +        x = np.zeros(
    +            (n_processes, order + samples_transient + n_samples, n_replications)
    +        )
     
             # Generate (different) initial conditions for each replication:
             # Uniformly sample from the [0,1] interval and tile as many times as
             # the VAR process order along the second dimension.
             x[:, 0:order, :] = np.tile(
    -            np.random.rand(n_processes, 1, n_replications),
    -            (1, order, 1)
    +            np.random.rand(n_processes, 1, n_replications), (1, order, 1)
             )
     
             # Compute time series
    @@ -1015,24 +1022,22 @@ 

    Source code for idtxl.data

                     for i_delay in range(1, order + 1):
                         x[:, i_sample, i_repl] += np.dot(
                             coefficient_matrices[i_delay - 1, :, :],
    -                        x[:, i_sample - i_delay, i_repl]
    +                        x[:, i_sample - i_delay, i_repl],
                         )
                     # Add uncorrelated Gaussian noise vector
                     x[:, i_sample, i_repl] += np.random.normal(
    -                    0,  # mean
    -                    noise_std,
    -                    x[:, i_sample, i_repl].shape
    +                    0, noise_std, x[:, i_sample, i_repl].shape  # mean
                     )
     
             # Discard transient effects (only take end of time series)
    -        self.set_data(x[:, -(n_samples + 1):-1, :], 'psr')
    + self.set_data(x[:, -(n_samples + 1) : -1, :], "psr")
    [docs] def generate_logistic_maps_data( self, n_samples=1000, n_replications=10, coefficient_matrices=np.array([[[0.5, 0], [0.4, 0.5]]]), - noise_std=0.1 + noise_std=0.1, ): """Generate discrete-time coupled-logistic-maps time series. @@ -1067,18 +1072,15 @@

    Source code for idtxl.data

             # Initialise time series matrix. The 3 dimensions represent
             # (processes, samples, replications). Only the last n_samples will be
             # kept, in order to discard transient effects.
    -        x = np.zeros((
    -            n_processes,
    -            order + samples_transient + n_samples,
    -            n_replications
    -        ))
    +        x = np.zeros(
    +            (n_processes, order + samples_transient + n_samples, n_replications)
    +        )
     
             # Generate (different) initial conditions for each replication:
             # Uniformly sample from the [0,1] interval and tile as many times as
             # the stochastic process order along the second dimension.
             x[:, 0:order, :] = np.tile(
    -            np.random.rand(n_processes, 1, n_replications),
    -            (1, order, 1)
    +            np.random.rand(n_processes, 1, n_replications), (1, order, 1)
             )
     
             # Compute time series
    @@ -1087,21 +1089,19 @@ 

    Source code for idtxl.data

                     for i_delay in range(1, order + 1):
                         x[:, i_sample, i_repl] += np.dot(
                             coefficient_matrices[i_delay - 1, :, :],
    -                        x[:, i_sample - i_delay, i_repl]
    +                        x[:, i_sample - i_delay, i_repl],
                         )
                     # Compute activation function
                     x[:, i_sample, i_repl] = f(x[:, i_sample, i_repl])
                     # Add uncorrelated Gaussian noise vector
                     x[:, i_sample, i_repl] += np.random.normal(
    -                    0,  # mean
    -                    noise_std,
    -                    x[:, i_sample, i_repl].shape
    +                    0, noise_std, x[:, i_sample, i_repl].shape  # mean
                     )
                     # ensure values are in the [0, 1] range
                     x[:, i_sample, i_repl] = x[:, i_sample, i_repl] % 1
     
             # Discard transient effects (only take end of time series)
    -        self.set_data(x[:, -(n_samples + 1):-1, :], 'psr')
    + self.set_data(x[:, -(n_samples + 1) : -1, :], "psr")
    @@ -1114,7 +1114,7 @@

    Source code for idtxl.data

       

    Quick search

    @@ -1133,14 +1133,14 @@

    Navigation

  • modules |
  • - +
    \ No newline at end of file diff --git a/docs/html/_modules/idtxl/embedding_optimization_ais_Rudelt.html b/docs/html/_modules/idtxl/embedding_optimization_ais_Rudelt.html index 4f526f5f..78886841 100644 --- a/docs/html/_modules/idtxl/embedding_optimization_ais_Rudelt.html +++ b/docs/html/_modules/idtxl/embedding_optimization_ais_Rudelt.html @@ -5,14 +5,13 @@ - idtxl.embedding_optimization_ais_Rudelt — IDTxl 1.4 documentation - - - + idtxl.embedding_optimization_ais_Rudelt — IDTxl 1.5.1 documentation + + + - @@ -32,7 +31,7 @@

    Navigation

  • modules |
  • - + @@ -57,7 +56,7 @@

    Source code for idtxl.embedding_optimization_ais_Rudelt

    # noinspection PyAttributeOutsideInit -
    [docs]class OptimizationRudelt(): +
    [docs]class OptimizationRudelt: """ Optimization of embedding parameters of spike times using the history dependence estimators @@ -180,47 +179,110 @@

    Source code for idtxl.embedding_optimization_ais_Rudelt

    settings = self._check_settings(settings) self.settings = settings.copy() - self.settings.setdefault('embedding_step_size', 0.005) - self.settings.setdefault('embedding_past_range_set', [0.005, 0.00561, 0.00629, 0.00706, 0.00792, 0.00889, - 0.00998, 0.01119, 0.01256, 0.01409, 0.01581, 0.01774, - 0.01991, 0.02233, 0.02506, 0.02812, 0.03155, 0.0354, - 0.03972, 0.04456, 0.05, 0.0561, 0.06295, 0.07063, - 0.07924, 0.08891, 0.09976, 0.11194, 0.12559, 0.14092, - 0.15811, 0.17741, 0.19905, 0.22334, 0.25059, 0.28117, - 0.31548, 0.35397, 0.39716, 0.44563, 0.5, 0.56101, 0.62946, - 0.70627, 0.79245, 0.88914, 0.99763, 1.11936, 1.25594, - 1.40919, 1.58114, 1.77407, 1.99054, 2.23342, 2.50594, - 2.81171, 3.15479, 3.53973, 3.97164, 4.45625, 5.0]) - self.settings.setdefault('embedding_number_of_bins_set', [1, 2, 3, 4, 5]) - self.settings.setdefault('embedding_scaling_exponent_set', - {'number_of_scalings': 10, - 'min_first_bin_size': 0.005, - 'min_step_for_scaling': 0.01}) - - self.settings.setdefault('bbc_tolerance', 0.05) - self.settings.setdefault('return_averaged_R', True) - self.settings.setdefault('timescale_minimum_past_range', 0.01) - - self.settings.setdefault('analyse_auto_MI', True) - self.settings.setdefault('auto_MI_bin_size_set', [0.005, 0.01, 0.025, 0.05, 0.25, 0.5]) - self.settings.setdefault('auto_MI_max_delay', 5) - - self.settings.setdefault('number_of_bootstraps_R_max', 250) - self.settings.setdefault('number_of_bootstraps_R_tot', 250) - self.settings.setdefault('number_of_bootstraps_nonessential', 0) - self.settings.setdefault('symbol_block_length', None) - self.settings.setdefault('bootstrap_CI_use_sd', True) - self.settings.setdefault('bootstrap_CI_percentile_lo', 2.5) - self.settings.setdefault('bootstrap_CI_percentile_hi', 97.5) - self.settings.setdefault('visualization', False) - self.settings.setdefault('debug', False) - - self.check_inputs() # ------------------------------------------------------------- TODO CHECK INPUTS - - self.embeddings = self.get_embeddings(self.settings['embedding_past_range_set'], - self.settings['embedding_number_of_bins_set'], - self.settings['embedding_scaling_exponent_set']) - + self.settings.setdefault("embedding_step_size", 0.005) + self.settings.setdefault( + "embedding_past_range_set", + [ + 0.005, + 0.00561, + 0.00629, + 0.00706, + 0.00792, + 0.00889, + 0.00998, + 0.01119, + 0.01256, + 0.01409, + 0.01581, + 0.01774, + 0.01991, + 0.02233, + 0.02506, + 0.02812, + 0.03155, + 0.0354, + 0.03972, + 0.04456, + 0.05, + 0.0561, + 0.06295, + 0.07063, + 0.07924, + 0.08891, + 0.09976, + 0.11194, + 0.12559, + 0.14092, + 0.15811, + 0.17741, + 0.19905, + 0.22334, + 0.25059, + 0.28117, + 0.31548, + 0.35397, + 0.39716, + 0.44563, + 0.5, + 0.56101, + 0.62946, + 0.70627, + 0.79245, + 0.88914, + 0.99763, + 1.11936, + 1.25594, + 1.40919, + 1.58114, + 1.77407, + 1.99054, + 2.23342, + 2.50594, + 2.81171, + 3.15479, + 3.53973, + 3.97164, + 4.45625, + 5.0, + ], + ) + self.settings.setdefault("embedding_number_of_bins_set", [1, 2, 3, 4, 5]) + self.settings.setdefault( + "embedding_scaling_exponent_set", + { + "number_of_scalings": 10, + "min_first_bin_size": 0.005, + "min_step_for_scaling": 0.01, + }, + ) + + self.settings.setdefault("bbc_tolerance", 0.05) + self.settings.setdefault("return_averaged_R", True) + self.settings.setdefault("timescale_minimum_past_range", 0.01) + + self.settings.setdefault("analyse_auto_MI", True) + self.settings.setdefault( + "auto_MI_bin_size_set", [0.005, 0.01, 0.025, 0.05, 0.25, 0.5] + ) + self.settings.setdefault("auto_MI_max_delay", 5) + + self.settings.setdefault("number_of_bootstraps_R_max", 250) + self.settings.setdefault("number_of_bootstraps_R_tot", 250) + self.settings.setdefault("number_of_bootstraps_nonessential", 0) + self.settings.setdefault("symbol_block_length", None) + self.settings.setdefault("bootstrap_CI_use_sd", True) + self.settings.setdefault("bootstrap_CI_percentile_lo", 2.5) + self.settings.setdefault("bootstrap_CI_percentile_hi", 97.5) + self.settings.setdefault("visualization", False) + self.settings.setdefault("debug", False) + + self.check_inputs() # ------------------------------------------------------------- TODO CHECK INPUTS + + self.embeddings = self.get_embeddings( + self.settings["embedding_past_range_set"], + self.settings["embedding_number_of_bins_set"], + self.settings["embedding_scaling_exponent_set"], + ) @staticmethod def _check_settings(settings=None): @@ -233,7 +295,7 @@

    Source code for idtxl.embedding_optimization_ais_Rudelt

    if settings is None: return {} elif type(settings) is not dict: - raise TypeError('settings should be a dictionary.') + raise TypeError("settings should be a dictionary.") else: return settings @@ -243,110 +305,149 @@

    Source code for idtxl.embedding_optimization_ais_Rudelt

    """ - args_float = ['embedding_step_size', - 'timescale_minimum_past_range', - 'bootstrap_CI_percentile_lo', - 'bootstrap_CI_percentile_hi', - 'bbc_tolerance'] + args_float = [ + "embedding_step_size", + "timescale_minimum_past_range", + "bootstrap_CI_percentile_lo", + "bootstrap_CI_percentile_hi", + "bbc_tolerance", + ] for key in args_float: - assert (key in self.settings), \ - (key + ' has to be specified (see help)!') - assert (isinstance(self.settings[key], float)), \ - ("Error: setting '" + key + "' needs to an floating point value (see help!!). Aborting.") - - args_int = ['number_of_bootstraps_R_max', - 'number_of_bootstraps_R_tot', - 'number_of_bootstraps_nonessential'] + assert key in self.settings, key + " has to be specified (see help)!" + assert isinstance(self.settings[key], float), ( + "Error: setting '" + + key + + "' needs to an floating point value (see help!!). Aborting." + ) + + args_int = [ + "number_of_bootstraps_R_max", + "number_of_bootstraps_R_tot", + "number_of_bootstraps_nonessential", + ] for key in args_int: - assert (key in self.settings), \ - (key + ' has to be specified (see help)!') - assert (isinstance(self.settings[key], int)), \ - ("Error: setting '" + key + "' needs to an integer value (see help!!). Aborting.") - - args_bool = ['return_averaged_R', - 'bootstrap_CI_use_sd', - 'debug'] + assert key in self.settings, key + " has to be specified (see help)!" + assert isinstance(self.settings[key], int), ( + "Error: setting '" + + key + + "' needs to an integer value (see help!!). Aborting." + ) + + args_bool = ["return_averaged_R", "bootstrap_CI_use_sd", "debug"] for key in args_bool: - assert (key in self.settings), \ - (key + ' has to be specified (see help)!') - assert (isinstance(self.settings[key], bool)), \ - ("Error: setting '" + key + "' needs to a boolean expression (see help!!). Aborting.") - - assert('embedding_past_range_set' in self.settings), \ - 'embedding_past_range_set has to be specified (see help)!' - assert (isinstance(self.settings['embedding_past_range_set'], list)), \ - "Error: setting 'embedding_past_range_set' needs to be a list but is defined as {0}. " \ - "Aborting.".format(type(self.settings['embedding_past_range_set'])) - - assert ('embedding_number_of_bins_set' in self.settings), \ - 'embedding_number_of_bins_set has to be specified (see help)!' - assert (isinstance(self.settings['embedding_number_of_bins_set'], list)), \ - "Error: setting 'embedding_number_of_bins_set' needs to a list but is defined as {0}. " \ - "Aborting.".format(type(self.settings['embedding_number_of_bins_set'])) - - assert ('embedding_scaling_exponent_set' in self.settings), \ - 'embedding_scaling_exponent_set has to be specified (see help)!' - - assert ('number_of_scalings' in self.settings['embedding_scaling_exponent_set']), \ - "'number_of_scalings' have to be specified in self.settings['embedding_scaling_exponent_set'] (see help)!" - assert (isinstance(self.settings['embedding_scaling_exponent_set']['number_of_scalings'], int)), \ - "Error: setting 'number_of_scalings' needs to be an integer value but is defined as {0}. " \ - "Aborting.".format(type(self.settings['number_of_scalings'])) - assert ('min_first_bin_size' in self.settings['embedding_scaling_exponent_set']), \ - "'min_first_bin_size' have to be specified in self.settings['embedding_scaling_exponent_set'] (see help)!" - assert (isinstance(self.settings['embedding_scaling_exponent_set']['min_first_bin_size'], float)), \ - "Error: setting 'min_first_bin_sizes' needs to be a floating point value but is defined as {0}. " \ - "Aborting.".format(type(self.settings['min_first_bin_sizes'])) - assert ('min_step_for_scaling' in self.settings['embedding_scaling_exponent_set']), \ - "'min_step_for_scaling' have to be specified in self.settings['embedding_scaling_exponent_set'] (see help)!" - assert (isinstance(self.settings['embedding_scaling_exponent_set']['min_step_for_scaling'], float)), \ - "Error: setting 'min_step_for_scaling' needs to be a floating point value but is defined as {0}. " \ - "Aborting.".format(type(self.settings['min_step_for_scaling'])) - - assert ('symbol_block_length' in self.settings), \ - 'symbol_block_length has to be specified (see help)!' - if self.settings['symbol_block_length'] is not None: - assert (isinstance(self.settings['symbol_block_length'], int)), \ - "Error: setting 'symbol_block_length' needs to an integer value but is defined as {0}. " \ - "Aborting.".format(type(self.settings['symbol_block_length'])) - - if self.settings['analyse_auto_MI']: - assert (isinstance(self.settings['analyse_auto_MI'], bool)), \ - "Error: setting 'analyse_auto_MI' needs to be a boolean expression but is defined as {0}. " \ - "Aborting.".format(type(self.settings['analyse_auto_MI'])) - assert ('auto_MI_bin_size_set' in self.settings), \ - 'If analyse_auto_MI is set to True, auto_MI_bin_size_set has to be specified (see help)!' - assert (isinstance(self.settings['auto_MI_bin_size_set'], list)), \ - "Error: setting 'auto_MI_bin_size_set' needs to a list but is defined as {0}. " \ - "Aborting.".format(type(self.settings['auto_MI_bin_size_set'])) - assert ('auto_MI_max_delay' in self.settings), \ - 'If analyse_auto_MI is set to True, auto_MI_max_delay has to be specified (see help)!' - assert (isinstance(self.settings['auto_MI_max_delay'], int)), \ - "Error: setting 'auto_MI_max_delay' needs to be an integer value but is defined as {0}. " \ - "Aborting.".format(type(self.settings['auto_MI_max_delay'])) - - if self.settings['visualization']: - assert (isinstance(self.settings['visualization'], bool)), \ - "Error: setting 'visualization' needs to be boolean but is defined as {0}. " \ - "Aborting.".format(type(self.settings['visualization'])) - assert ('output_path' in self.settings), \ - 'If visualization is set to True an output path has to be specified (see help)!' - assert ('output_prefix' in self.settings), \ - 'If visualization is set to True an output prefix has to be specified (see help)!' + assert key in self.settings, key + " has to be specified (see help)!" + assert isinstance(self.settings[key], bool), ( + "Error: setting '" + + key + + "' needs to a boolean expression (see help!!). Aborting." + ) + + assert ( + "embedding_past_range_set" in self.settings + ), "embedding_past_range_set has to be specified (see help)!" + assert isinstance(self.settings["embedding_past_range_set"], list), ( + "Error: setting 'embedding_past_range_set' needs to be a list but is defined as {0}. " + "Aborting.".format(type(self.settings["embedding_past_range_set"])) + ) + + assert ( + "embedding_number_of_bins_set" in self.settings + ), "embedding_number_of_bins_set has to be specified (see help)!" + assert isinstance(self.settings["embedding_number_of_bins_set"], list), ( + "Error: setting 'embedding_number_of_bins_set' needs to a list but is defined as {0}. " + "Aborting.".format(type(self.settings["embedding_number_of_bins_set"])) + ) + + assert ( + "embedding_scaling_exponent_set" in self.settings + ), "embedding_scaling_exponent_set has to be specified (see help)!" + + assert ( + "number_of_scalings" in self.settings["embedding_scaling_exponent_set"] + ), "'number_of_scalings' have to be specified in self.settings['embedding_scaling_exponent_set'] (see help)!" + assert isinstance( + self.settings["embedding_scaling_exponent_set"]["number_of_scalings"], int + ), ( + "Error: setting 'number_of_scalings' needs to be an integer value but is defined as {0}. " + "Aborting.".format(type(self.settings["number_of_scalings"])) + ) + assert ( + "min_first_bin_size" in self.settings["embedding_scaling_exponent_set"] + ), "'min_first_bin_size' have to be specified in self.settings['embedding_scaling_exponent_set'] (see help)!" + assert isinstance( + self.settings["embedding_scaling_exponent_set"]["min_first_bin_size"], float + ), ( + "Error: setting 'min_first_bin_sizes' needs to be a floating point value but is defined as {0}. " + "Aborting.".format(type(self.settings["min_first_bin_sizes"])) + ) + assert ( + "min_step_for_scaling" in self.settings["embedding_scaling_exponent_set"] + ), "'min_step_for_scaling' have to be specified in self.settings['embedding_scaling_exponent_set'] (see help)!" + assert isinstance( + self.settings["embedding_scaling_exponent_set"]["min_step_for_scaling"], + float, + ), ( + "Error: setting 'min_step_for_scaling' needs to be a floating point value but is defined as {0}. " + "Aborting.".format(type(self.settings["min_step_for_scaling"])) + ) + + assert ( + "symbol_block_length" in self.settings + ), "symbol_block_length has to be specified (see help)!" + if self.settings["symbol_block_length"] is not None: + assert isinstance(self.settings["symbol_block_length"], int), ( + "Error: setting 'symbol_block_length' needs to an integer value but is defined as {0}. " + "Aborting.".format(type(self.settings["symbol_block_length"])) + ) + + if self.settings["analyse_auto_MI"]: + assert isinstance(self.settings["analyse_auto_MI"], bool), ( + "Error: setting 'analyse_auto_MI' needs to be a boolean expression but is defined as {0}. " + "Aborting.".format(type(self.settings["analyse_auto_MI"])) + ) + assert ( + "auto_MI_bin_size_set" in self.settings + ), "If analyse_auto_MI is set to True, auto_MI_bin_size_set has to be specified (see help)!" + assert isinstance(self.settings["auto_MI_bin_size_set"], list), ( + "Error: setting 'auto_MI_bin_size_set' needs to a list but is defined as {0}. " + "Aborting.".format(type(self.settings["auto_MI_bin_size_set"])) + ) + assert ( + "auto_MI_max_delay" in self.settings + ), "If analyse_auto_MI is set to True, auto_MI_max_delay has to be specified (see help)!" + assert isinstance(self.settings["auto_MI_max_delay"], int), ( + "Error: setting 'auto_MI_max_delay' needs to be an integer value but is defined as {0}. " + "Aborting.".format(type(self.settings["auto_MI_max_delay"])) + ) + + if self.settings["visualization"]: + assert isinstance(self.settings["visualization"], bool), ( + "Error: setting 'visualization' needs to be boolean but is defined as {0}. " + "Aborting.".format(type(self.settings["visualization"])) + ) + assert ( + "output_path" in self.settings + ), "If visualization is set to True an output path has to be specified (see help)!" + assert ( + "output_prefix" in self.settings + ), "If visualization is set to True an output prefix has to be specified (see help)!" # Cython implementation uses 64bit unsigned integers for the symbols, # we allow up to 62 bins (window has 1 bin more..) - assert (max(self.settings['embedding_number_of_bins_set']) <= 62), \ - 'Error: Max number of bins too large; use less than 63. Aborting.' + assert ( + max(self.settings["embedding_number_of_bins_set"]) <= 62 + ), "Error: Max number of bins too large; use less than 63. Aborting." # If R_tot is computed as an average over Rs, no confidence interval can be estimated - if self.settings['return_averaged_R']: - self.settings['number_of_bootstraps_R_tot'] = 0
    - -
    [docs] def get_embeddings(self, - embedding_past_range_set, - embedding_number_of_bins_set, - embedding_scaling_exponent_set): + if self.settings["return_averaged_R"]: + self.settings["number_of_bootstraps_R_tot"] = 0
    + +
    [docs] def get_embeddings( + self, + embedding_past_range_set, + embedding_number_of_bins_set, + embedding_scaling_exponent_set, + ): """ Get all combinations of parameters T, d, k, based on the sets of selected parameters. @@ -356,14 +457,19 @@

    Source code for idtxl.embedding_optimization_ais_Rudelt

    for past_range_T in embedding_past_range_set: for number_of_bins_d in embedding_number_of_bins_set: if not isinstance(number_of_bins_d, int) or number_of_bins_d < 1: - print("Error: number of bins {} is not a positive integer. Skipping.".format(number_of_bins_d), - file=stderr, flush=True) + print( + "Error: number of bins {} is not a positive integer. Skipping.".format( + number_of_bins_d + ), + file=stderr, + flush=True, + ) continue if type(embedding_scaling_exponent_set) == dict: - scaling_set_given_T_and_d = self.get_set_of_scalings(past_range_T, - number_of_bins_d, - **embedding_scaling_exponent_set) + scaling_set_given_T_and_d = self.get_set_of_scalings( + past_range_T, number_of_bins_d, **embedding_scaling_exponent_set + ) else: scaling_set_given_T_and_d = embedding_scaling_exponent_set @@ -372,11 +478,14 @@

    Source code for idtxl.embedding_optimization_ais_Rudelt

    return embeddings
    -
    [docs] def get_set_of_scalings(self, past_range_T, - number_of_bins_d, - number_of_scalings, - min_first_bin_size, - min_step_for_scaling): +
    [docs] def get_set_of_scalings( + self, + past_range_T, + number_of_bins_d, + number_of_scalings, + min_first_bin_size, + min_step_for_scaling, + ): """ Get scaling exponents such that the uniform embedding as well as the embedding for which the first bin has a length of @@ -386,19 +495,28 @@

    Source code for idtxl.embedding_optimization_ais_Rudelt

    """ min_scaling = 0 - if past_range_T / number_of_bins_d <= min_first_bin_size or number_of_bins_d == 1: + if ( + past_range_T / number_of_bins_d <= min_first_bin_size + or number_of_bins_d == 1 + ): max_scaling = 0 else: # for the initial guess assume the largest bin dominates, so k is approx. log(T) / d - max_scaling = newton(lambda scaling: self.get_past_range(number_of_bins_d, - min_first_bin_size, - scaling) - past_range_T, - np.log10(past_range_T / min_first_bin_size) / (number_of_bins_d - 1), - tol=1e-04, maxiter=500) - - while np.linspace(min_scaling, max_scaling, - number_of_scalings, retstep=True)[1] < min_step_for_scaling: + max_scaling = newton( + lambda scaling: self.get_past_range( + number_of_bins_d, min_first_bin_size, scaling + ) + - past_range_T, + np.log10(past_range_T / min_first_bin_size) / (number_of_bins_d - 1), + tol=1e-04, + maxiter=500, + ) + + while ( + np.linspace(min_scaling, max_scaling, number_of_scalings, retstep=True)[1] + < min_step_for_scaling + ): number_of_scalings -= 1 return np.linspace(min_scaling, max_scaling, number_of_scalings)
    @@ -408,67 +526,82 @@

    Source code for idtxl.embedding_optimization_ais_Rudelt

    Get the past range T of the embedding, based on the parameters d, tau_1 and k. """ - return np.sum([first_bin_size * 10 ** ((number_of_bins_d - i) * scaling_k) - for i in range(1, number_of_bins_d + 1)])
    + return np.sum( + [ + first_bin_size * 10 ** ((number_of_bins_d - i) * scaling_k) + for i in range(1, number_of_bins_d + 1) + ] + )
    [docs] def get_history_dependence(self, data, process): """ - Estimate the history dependence for each embedding to all given processes. + Estimate the history dependence for each embedding to all given processes. """ # load estimators - if self.settings['estimation_method'] == 'bbc': + if self.settings["estimation_method"] == "bbc": estbbc = RudeltBBCEstimator() - elif self.settings['estimation_method'] == 'shuffling': + elif self.settings["estimation_method"] == "shuffling": estshu = RudeltShufflingEstimator() # get history dependence history_dependence = np.empty(shape=(len(self.embeddings))) - if self.settings['estimation_method'] == 'bbc': + if self.settings["estimation_method"] == "bbc": bbc_term = np.empty(shape=(len(self.embeddings))) embedding_count = 0 for embedding in self.embeddings: - - if self.settings['debug']: - print("Embedding: " + str(embedding[0]) + ", " + str(embedding[1]) + ", " + str(embedding[2])) - - symbol_array, past_symbol_array, current_symbol_array, symbol_array_length = \ - data.get_realisations_symbols(process, - embedding[0], - embedding[1], - embedding[2], - self.settings['embedding_step_size'], - output_spike_times=False) - - if self.settings['estimation_method'] == 'bbc': - I_bbc, R_bbc, bbc_t = \ - estbbc.estimate(symbol_array[0], past_symbol_array[0], current_symbol_array[0]) + if self.settings["debug"]: + print( + "Embedding: " + + str(embedding[0]) + + ", " + + str(embedding[1]) + + ", " + + str(embedding[2]) + ) + + ( + symbol_array, + past_symbol_array, + current_symbol_array, + symbol_array_length, + ) = data.get_realisations_symbols( + process, + embedding[0], + embedding[1], + embedding[2], + self.settings["embedding_step_size"], + output_spike_times=False, + ) + + if self.settings["estimation_method"] == "bbc": + I_bbc, R_bbc, bbc_t = estbbc.estimate( + symbol_array[0], past_symbol_array[0], current_symbol_array[0] + ) history_dependence[embedding_count] = R_bbc bbc_term[embedding_count] = bbc_t - if self.settings['debug']: + if self.settings["debug"]: print("\tHD: " + str(R_bbc) + " BBC: " + str(bbc_t)) - elif self.settings['estimation_method'] == 'shuffling': + elif self.settings["estimation_method"] == "shuffling": I_sh, R_sh = estshu.estimate(symbol_array[0]) history_dependence[embedding_count] = R_sh - if self.settings['debug']: + if self.settings["debug"]: print("\tHD: " + str(R_sh)) embedding_count += 1 - if self.settings['estimation_method'] == 'bbc': + if self.settings["estimation_method"] == "bbc": return history_dependence, bbc_term - elif self.settings['estimation_method'] == 'shuffling': + elif self.settings["estimation_method"] == "shuffling": return history_dependence
    -
    [docs] def get_bootstrap_history_dependence(self, - data, - embedding, - number_of_bootstraps, - symbol_block_length=None): +
    [docs] def get_bootstrap_history_dependence( + self, data, embedding, number_of_bootstraps, symbol_block_length=None + ): """ For a given embedding, return bootstrap replications for R. """ @@ -482,27 +615,35 @@

    Source code for idtxl.embedding_optimization_ais_Rudelt

    bs_Rs = np.zeros(number_of_bootstraps) for rep in range(number_of_bootstraps): - bs_symbol_array, bs_past_symbol_array, bs_current_symbol_array = \ - data.get_bootstrap_realisations_symbols(self.process, - embedding[0], - embedding[1], - embedding[2], - self.settings['embedding_step_size'], - symbol_block_length=symbol_block_length) - - if self.settings['estimation_method'] == 'bbc': - I_bbc, R_bbc, bbc_t = \ - estbbc.estimate(bs_symbol_array[0], bs_past_symbol_array[0], bs_current_symbol_array[0]) + ( + bs_symbol_array, + bs_past_symbol_array, + bs_current_symbol_array, + ) = data.get_bootstrap_realisations_symbols( + self.process, + embedding[0], + embedding[1], + embedding[2], + self.settings["embedding_step_size"], + symbol_block_length=symbol_block_length, + ) + + if self.settings["estimation_method"] == "bbc": + I_bbc, R_bbc, bbc_t = estbbc.estimate( + bs_symbol_array[0], + bs_past_symbol_array[0], + bs_current_symbol_array[0], + ) bs_Rs[rep] = R_bbc - if self.settings['debug']: + if self.settings["debug"]: print("\tHD: " + str(R_bbc) + " BBC: " + str(bbc_t)) - elif self.settings['estimation_method'] == 'shuffling': + elif self.settings["estimation_method"] == "shuffling": I_sh, R_sh = estshu.estimate(bs_symbol_array[0]) bs_Rs[rep] = R_sh - if self.settings['debug']: + if self.settings["debug"]: print("\tHD: " + str(R_sh)) return bs_Rs
    @@ -520,8 +661,7 @@

    Source code for idtxl.embedding_optimization_ais_Rudelt

    """ # load data - embedding_maximising_R_at_T, max_Rs \ - = self.get_embeddings_that_maximise_R() + embedding_maximising_R_at_T, max_Rs = self.get_embeddings_that_maximise_R() Ts = sorted([key for key in max_Rs.keys()]) Rs = [max_Rs[T] for T in Ts] @@ -553,10 +693,9 @@

    Source code for idtxl.embedding_optimization_ais_Rudelt

    else: return T_D, R_tot_thresh
    -
    [docs] def get_embeddings_that_maximise_R(self, - bbc_tolerance=None, - dependent_var="T", - get_as_list=False): +
    [docs] def get_embeddings_that_maximise_R( + self, bbc_tolerance=None, dependent_var="T", get_as_list=False + ): """ For each T (or d), get the embedding for which R is maximised. @@ -579,30 +718,46 @@

    Source code for idtxl.embedding_optimization_ais_Rudelt

    scaling_k = float(embedding[2]) history_dependence = self.history_dependence[i] - if self.settings['estimation_method'] == "bbc": - if self.bbc_term[i] >= self.settings['bbc_tolerance']: # ----------------------- TODO check + if self.settings["estimation_method"] == "bbc": + if ( + self.bbc_term[i] >= self.settings["bbc_tolerance"] + ): # ----------------------- TODO check continue if dependent_var == "T": - if not past_range_T in embeddings_that_maximise_R \ - or history_dependence > max_Rs[past_range_T]: + if ( + not past_range_T in embeddings_that_maximise_R + or history_dependence > max_Rs[past_range_T] + ): max_Rs[past_range_T] = history_dependence - embeddings_that_maximise_R[past_range_T] = (number_of_bins_d, scaling_k) + embeddings_that_maximise_R[past_range_T] = ( + number_of_bins_d, + scaling_k, + ) elif dependent_var == "d": - if not number_of_bins_d in embeddings_that_maximise_R \ - or history_dependence > max_Rs[number_of_bins_d]: + if ( + not number_of_bins_d in embeddings_that_maximise_R + or history_dependence > max_Rs[number_of_bins_d] + ): max_Rs[number_of_bins_d] = history_dependence - embeddings_that_maximise_R[number_of_bins_d] = (past_range_T, scaling_k) + embeddings_that_maximise_R[number_of_bins_d] = ( + past_range_T, + scaling_k, + ) if get_as_list: embeddings = [] if dependent_var == "T": for past_range_T in embeddings_that_maximise_R: - number_of_bins_d, scaling_k = embeddings_that_maximise_R[past_range_T] + number_of_bins_d, scaling_k = embeddings_that_maximise_R[ + past_range_T + ] embeddings += [(past_range_T, number_of_bins_d, scaling_k)] elif dependent_var == "d": for number_of_bins_d in embeddings_that_maximise_R: - past_range_T, scaling_k = embeddings_that_maximise_R[number_of_bins_d] + past_range_T, scaling_k = embeddings_that_maximise_R[ + number_of_bins_d + ] embeddings += [(past_range_T, number_of_bins_d, scaling_k)] return embeddings else: @@ -626,14 +781,13 @@

    Source code for idtxl.embedding_optimization_ais_Rudelt

    # get dRs dRs = [] - R_prev = 0. + R_prev = 0.0 # No values higher than R_tot are allowed, # otherwise the information timescale might be # misestimated because of spurious contributions # at large T for R, T in zip(Rs[Rs <= R_tot], Ts[Rs <= R_tot]): - # No negative increments are allowed dRs += [np.amax([0.0, R - R_prev])] @@ -641,8 +795,7 @@

    Source code for idtxl.embedding_optimization_ais_Rudelt

    if R > R_prev: R_prev = R - dRs = np.pad(dRs, (0, len(Rs) - len(dRs)), - mode='constant', constant_values=0) + dRs = np.pad(dRs, (0, len(Rs) - len(dRs)), mode="constant", constant_values=0) # compute tau_R Ts_0 = np.append([0], Ts) @@ -652,17 +805,14 @@

    Source code for idtxl.embedding_optimization_ais_Rudelt

    Ts_0 = Ts_0[Ts_0 >= T_0] norm = np.sum(dRs_0) - if norm == 0.: + if norm == 0.0: tau = 0.0 else: Ts_0 -= Ts_0[0] tau = np.dot(((Ts_0[:-1] + Ts_0[1:]) / 2), dRs_0) / norm return tau
    -
    [docs] def get_R_tot(self, - return_averaged_R=False, - **kwargs): - +
    [docs] def get_R_tot(self, return_averaged_R=False, **kwargs): max_Rs = self.max_Rs if return_averaged_R: @@ -682,15 +832,11 @@

    Source code for idtxl.embedding_optimization_ais_Rudelt

    return np.average([R for R, T in zip(Rs, Ts) if T_D <= T < T_max]) else: - temporal_depth_T_D = self.get_temporal_depth_T_D() return max_Rs[temporal_depth_T_D]
    -
    [docs] def compute_CIs(self, - data, - target_R='R_max', - symbol_block_length=None): +
    [docs] def compute_CIs(self, data, target_R="R_max", symbol_block_length=None): """ Compute bootstrap replications of the history dependence estimate which can be used to obtain confidence intervals. @@ -715,26 +861,25 @@

    Source code for idtxl.embedding_optimization_ais_Rudelt

    on heuristics """ - assert target_R in ['nonessential', 'R_max', 'R_tot'] + assert target_R in ["nonessential", "R_max", "R_tot"] - number_of_bootstraps = self.settings['number_of_bootstraps_{}'.format(target_R)] + number_of_bootstraps = self.settings["number_of_bootstraps_{}".format(target_R)] if number_of_bootstraps == 0: return - embedding_maximising_R_at_T, max_Rs \ - = self.get_embeddings_that_maximise_R() + embedding_maximising_R_at_T, max_Rs = self.get_embeddings_that_maximise_R() self.embedding_maximising_R_at_T = embedding_maximising_R_at_T self.max_Rs = max_Rs - if target_R == 'nonessential': + if target_R == "nonessential": # bootstrap R for unessential Ts (not required for the main analysis) embeddings = [] for past_range_T in embedding_maximising_R_at_T: number_of_bins_d, scaling_k = embedding_maximising_R_at_T[past_range_T] embeddings += [(past_range_T, number_of_bins_d, scaling_k)] - elif target_R == 'R_max': + elif target_R == "R_max": # bootstrap R for the max R, to get a good estimate for the standard deviation # which is used to determine R_tot max_R, max_R_T = utl.get_max_R_T(max_Rs) @@ -743,14 +888,13 @@

    Source code for idtxl.embedding_optimization_ais_Rudelt

    number_of_bins_d, scaling_k = embedding_maximising_R_at_T[max_R_T] embeddings = [(max_R_T, number_of_bins_d, scaling_k)] - elif target_R == 'R_tot': + elif target_R == "R_tot": T_D = self.get_temporal_depth_T_D() number_of_bins_d, scaling_k = embedding_maximising_R_at_T[T_D] embeddings = [(T_D, number_of_bins_d, scaling_k)] for embedding in embeddings: - embindex = self.embeddings.index(embedding) if hasattr(self, "bs_history_dependence"): @@ -767,11 +911,12 @@

    Source code for idtxl.embedding_optimization_ais_Rudelt

    if not number_of_bootstraps > number_of_stored_bootstraps: continue - bs_R = \ - self.get_bootstrap_history_dependence(data, - embedding, - number_of_bootstraps - number_of_stored_bootstraps, - symbol_block_length=symbol_block_length) + bs_R = self.get_bootstrap_history_dependence( + data, + embedding, + number_of_bootstraps - number_of_stored_bootstraps, + symbol_block_length=symbol_block_length, + ) if stored_bs_Rs is not None: bs_R = np.concatenate([stored_bs_Rs, bs_R]) @@ -784,13 +929,12 @@

    Source code for idtxl.embedding_optimization_ais_Rudelt

    it, else compute it. """ - auto_MI_data = { - "delay": [], - "auto_MI": [] - } + auto_MI_data = {"delay": [], "auto_MI": []} auto_MI_dict = {} - for auto_MI_bin_size in self.settings['auto_MI_bin_size_set']: - number_of_delays = int(self.settings['auto_MI_max_delay'] / auto_MI_bin_size) + 1 + for auto_MI_bin_size in self.settings["auto_MI_bin_size_set"]: + number_of_delays = ( + int(self.settings["auto_MI_max_delay"] / auto_MI_bin_size) + 1 + ) # perform the MI analysis auto_MI = self.get_auto_MI(spike_times, auto_MI_bin_size, number_of_delays) @@ -799,7 +943,7 @@

    Source code for idtxl.embedding_optimization_ais_Rudelt

    auto_MI_dict[auto_MI_bin_size] = auto_MI - auto_MI_data['auto_MI'] = auto_MI_dict + auto_MI_data["auto_MI"] = auto_MI_dict self.auto_MI = auto_MI_data
    [docs] def get_auto_MI(self, spike_times, bin_size, number_of_delays): @@ -808,38 +952,55 @@

    Source code for idtxl.embedding_optimization_ais_Rudelt

    measure closely related to history dependence. """ - binned_neuron_activity = utl.get_binned_neuron_activity(spike_times, bin_size, relative_to_median_activity=True) + binned_neuron_activity = utl.get_binned_neuron_activity( + spike_times, bin_size, relative_to_median_activity=True + ) p_spike = sum(binned_neuron_activity) / len(binned_neuron_activity) - self.H_spiking = utl.get_shannon_entropy([p_spike, - 1 - p_spike]) + self.H_spiking = utl.get_shannon_entropy([p_spike, 1 - p_spike]) auto_MIs = np.empty(number_of_delays) # compute auto MI for delay in range(number_of_delays): - symbol_counts = [] number_of_symbols = len(binned_neuron_activity) - delay - 1 - symbols = np.array([2 * binned_neuron_activity[i] + binned_neuron_activity[i + delay + 1] - for i in range(number_of_symbols)]) - symbol_counts += [dict([(unq_symbol, len(np.where(symbols == unq_symbol)[0])) - for unq_symbol in np.unique(symbols)])] + symbols = np.array( + [ + 2 * binned_neuron_activity[i] + + binned_neuron_activity[i + delay + 1] + for i in range(number_of_symbols) + ] + ) + symbol_counts += [ + dict( + [ + (unq_symbol, len(np.where(symbols == unq_symbol)[0])) + for unq_symbol in np.unique(symbols) + ] + ) + ] symbol_counts = utl.add_up_dicts(symbol_counts) number_of_symbols = sum(symbol_counts.values()) - H_joint = utl.get_shannon_entropy([number_of_occurrences / number_of_symbols - for number_of_occurrences in symbol_counts.values()]) + H_joint = utl.get_shannon_entropy( + [ + number_of_occurrences / number_of_symbols + for number_of_occurrences in symbol_counts.values() + ] + ) # I(X : Y) = H(X) - H(X|Y) = H(X) - (H(X,Y) - H(Y)) = H(X) + H(Y) - H(X,Y) # auto_MI = 2 * H_spiking - H_joint - auto_MIs[delay] = 2 - H_joint / self.H_spiking # normalized auto MI = auto MI / H_spiking + auto_MIs[delay] = ( + 2 - H_joint / self.H_spiking + ) # normalized auto MI = auto MI / H_spiking return auto_MIs
    -
    [docs] def optimize(self, data, processes='all'): +
    [docs] def optimize(self, data, processes="all"): """ Optimize the embedding parameters of spike time data using the Rudelt history dependence estimator. @@ -874,30 +1035,32 @@

    Source code for idtxl.embedding_optimization_ais_Rudelt

    # check input data if type(data) != Data_spiketime: - raise ValueError('Input Data nneds to be Data_spiketime object but is defined as: ' - '{0}.'.format(type(data))) + raise ValueError( + "Input Data nneds to be Data_spiketime object but is defined as: " + "{0}.".format(type(data)) + ) # check input process list - if processes == 'all': + if processes == "all": processes = [t for t in range(data.n_processes)] elif (type(processes) is list) and (type(processes[0]) is int): pass else: - raise ValueError('Processes were not specified correctly: ' - '{0}.'.format(processes)) + raise ValueError( + "Processes were not specified correctly: " "{0}.".format(processes) + ) - if self.settings['debug']: + if self.settings["debug"]: import pprint + pprint.pprint(self.settings, width=1) # open result dict - results = ResultsSingleProcessRudelt( - processes=processes) + results = ResultsSingleProcessRudelt(processes=processes) # start optimizing given processes process_count = 0 for process in processes: - # optimize single process single_result = self.optimize_single_run(data, process) @@ -906,13 +1069,15 @@

    Source code for idtxl.embedding_optimization_ais_Rudelt

    process_count=process_count, process=process, settings=self.settings, - results=single_result) + results=single_result, + ) process_count += 1 - if self.settings['visualization'] == True: - filename = Path(self.settings['output_path']).joinpath( - '{}_process{}.svg'.format(self.settings['output_prefix'], process)) + if self.settings["visualization"] == True: + filename = Path(self.settings["output_path"]).joinpath( + "{}_process{}.svg".format(self.settings["output_prefix"], process) + ) utl.hde_visualize_results(results, process, filename) # remove results from single process from self @@ -922,100 +1087,106 @@

    Source code for idtxl.embedding_optimization_ais_Rudelt

    [docs] def optimize_single_run(self, data, process): """ - optimizes a single realisation of spike time data given the process number - - Args: - data : Data_spiketime instance - raw data for analysis - process : int - index of process; - - Returns: - DotDict - with the following keys - - Process : int - Process that was optimized - estimation_method : String - Estimation method that was used for optimization - T_D : float - 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_tot : float - Estimated value for the total history dependence Rtot, - AIS_tot : float - Estimated value for the total active information storage - opt_number_of_bins_d : int - Number of bins d for the embedding that yields (R̂tot ,T̂D) - opt_scaling_k : int - Scaling exponent κ for the embedding that yields (R̂tot , T̂D) - 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 - Estimated history dependence for each embedding - firing_rate : float - Firing rate of the neuron/ spike train - recording_length : float - Length of the recording (in seconds) - H_spiking : float - 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 - auto_MI_delays : list of int - list of delays depending on the given auto_MI_bin_sizes and auto_MI_max_delay + optimizes a single realisation of spike time data given the process number + + Args: + data : Data_spiketime instance + raw data for analysis + process : int + index of process; + + Returns: + DotDict + with the following keys + + Process : int + Process that was optimized + estimation_method : String + Estimation method that was used for optimization + T_D : float + 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_tot : float + Estimated value for the total history dependence Rtot, + AIS_tot : float + Estimated value for the total active information storage + opt_number_of_bins_d : int + Number of bins d for the embedding that yields (R̂tot ,T̂D) + opt_scaling_k : int + Scaling exponent κ for the embedding that yields (R̂tot , T̂D) + 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 + Estimated history dependence for each embedding + firing_rate : float + Firing rate of the neuron/ spike train + recording_length : float + Length of the recording (in seconds) + H_spiking : float + 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 + auto_MI_delays : list of int + list of delays depending on the given auto_MI_bin_sizes and auto_MI_max_delay """ - if (type(process) is int): + if type(process) is int: pass else: - raise ValueError('Process is not specified correctly: ' - '{0}.'.format(process)) + raise ValueError( + "Process is not specified correctly: " "{0}.".format(process) + ) self.process = process # get history dependence - if self.settings['debug']: + if self.settings["debug"]: print("\n\nGet History dependence\n") - if self.settings['estimation_method'] == 'bbc': - self.history_dependence, self.bbc_term = \ - self.get_history_dependence(data, process) - elif self.settings['estimation_method'] == 'shuffling': - self.history_dependence = \ - self.get_history_dependence(data, process) + if self.settings["estimation_method"] == "bbc": + self.history_dependence, self.bbc_term = self.get_history_dependence( + data, process + ) + elif self.settings["estimation_method"] == "shuffling": + self.history_dependence = self.get_history_dependence(data, process) - if self.settings['debug']: + if self.settings["debug"]: print("\n\nCompute CI\n") # get bootstrap history dependence (CI) - if self.settings['debug']: + if self.settings["debug"]: print("R_max") - self.compute_CIs(data, - target_R='R_max', - symbol_block_length=self.settings['symbol_block_length']) - if self.settings['debug']: + self.compute_CIs( + data, + target_R="R_max", + symbol_block_length=self.settings["symbol_block_length"], + ) + if self.settings["debug"]: print("R_tot") - self.compute_CIs(data, - target_R='R_tot', - symbol_block_length=self.settings['symbol_block_length']) - if self.settings['debug']: + self.compute_CIs( + data, + target_R="R_tot", + symbol_block_length=self.settings["symbol_block_length"], + ) + if self.settings["debug"]: print("R_nonessential") - self.compute_CIs(data, - target_R='nonessential', - symbol_block_length=self.settings['symbol_block_length']) + self.compute_CIs( + data, + target_R="nonessential", + symbol_block_length=self.settings["symbol_block_length"], + ) # analyse auto MI - if self.settings['analyse_auto_MI']: - if self.settings['debug']: + if self.settings["analyse_auto_MI"]: + if self.settings["debug"]: print("\nAnalyse auto MI\n") spike_times = data.get_spike_times_single(process) self.analyse_auto_MI(spike_times) - # get output values T_D = self.get_temporal_depth_T_D() tau_R = self.get_information_timescale_tau_R() @@ -1024,63 +1195,70 @@

    Source code for idtxl.embedding_optimization_ais_Rudelt

    max_Rs = self.max_Rs mr = np.array(list(max_Rs.items()), dtype=float) HD_max_R = mr[:, 1] - opt_first_bin_size = newton(lambda first_bin_size: self.get_past_range(opt_number_of_bins_d, - first_bin_size, - opt_scaling_k) - T_D, - 0.005, tol=1e-03, maxiter=100) - firing_rate = data.get_firingrate(process, self.settings['embedding_step_size']) + opt_first_bin_size = newton( + lambda first_bin_size: self.get_past_range( + opt_number_of_bins_d, first_bin_size, opt_scaling_k + ) + - T_D, + 0.005, + tol=1e-03, + maxiter=100, + ) + firing_rate = data.get_firingrate(process, self.settings["embedding_step_size"]) recording_length = data.get_recording_length(process) - H_spiking = data.get_H_spiking(process, self.settings['embedding_step_size']) + H_spiking = data.get_H_spiking(process, self.settings["embedding_step_size"]) # get CI bounds - if not self.settings['return_averaged_R']: - embedding = (T_D, - opt_number_of_bins_d, - opt_scaling_k) + if not self.settings["return_averaged_R"]: + embedding = (T_D, opt_number_of_bins_d, opt_scaling_k) emb_ind = self.embeddings.index(embedding) - R_tot_CI_lo, R_tot_CI_hi = utl.get_CI_bounds(R_tot, - self.bs_history_dependence[emb_ind], - self.settings['bootstrap_CI_use_sd'], - self.settings['bootstrap_CI_percentile_lo'], - self.settings['bootstrap_CI_percentile_hi']) + R_tot_CI_lo, R_tot_CI_hi = utl.get_CI_bounds( + R_tot, + self.bs_history_dependence[emb_ind], + self.settings["bootstrap_CI_use_sd"], + self.settings["bootstrap_CI_percentile_lo"], + self.settings["bootstrap_CI_percentile_hi"], + ) else: R_tot_CI_lo = None R_tot_CI_hi = None R_tot_CI = [R_tot_CI_lo, R_tot_CI_hi] - if self.settings['debug']: - print('Process: ' + str(process)) - print('T_D: ' + str(T_D)) - print('tau_R: ' + str(tau_R)) - print('R_tot: ' + str(R_tot)) - print('R_tot_CI: ' + str(R_tot_CI)) - print('opt_number_of_bins_d: ' + str(opt_number_of_bins_d)) - print('opt_scaling_k: ' + str(opt_scaling_k)) - print('opt_first_bin_size: ' + str(opt_first_bin_size)) - print('firing_rate: ' + str(firing_rate)) - print('recording_length: ' + str(recording_length)) - print('H_spiking: ' + str(H_spiking)) + if self.settings["debug"]: + print("Process: " + str(process)) + print("T_D: " + str(T_D)) + print("tau_R: " + str(tau_R)) + print("R_tot: " + str(R_tot)) + print("R_tot_CI: " + str(R_tot_CI)) + print("opt_number_of_bins_d: " + str(opt_number_of_bins_d)) + print("opt_scaling_k: " + str(opt_scaling_k)) + print("opt_first_bin_size: " + str(opt_first_bin_size)) + print("firing_rate: " + str(firing_rate)) + print("recording_length: " + str(recording_length)) + print("H_spiking: " + str(H_spiking)) # create output dict - results = {'Process': process, - 'estimation_method': self.settings['estimation_method'], - 'T_D': T_D, - 'tau_R': tau_R, - 'R_tot': R_tot, - 'R_tot_CI': R_tot_CI, - 'AIS_tot': R_tot * H_spiking, - 'opt_number_of_bins_d': opt_number_of_bins_d, - 'opt_scaling_k': opt_scaling_k, - 'opt_first_bin_size': opt_first_bin_size, - 'history_dependence': self.history_dependence, - 'firing_rate': firing_rate, - 'recording_length': recording_length, - 'H_spiking': H_spiking, - 'max_R': max_Rs} - - if self.settings['analyse_auto_MI']: - results['auto_MI'] = self.auto_MI.get('auto_MI') - results['auto_MI_delays'] = self.auto_MI.get('delay') + results = { + "Process": process, + "estimation_method": self.settings["estimation_method"], + "T_D": T_D, + "tau_R": tau_R, + "R_tot": R_tot, + "R_tot_CI": R_tot_CI, + "AIS_tot": R_tot * H_spiking, + "opt_number_of_bins_d": opt_number_of_bins_d, + "opt_scaling_k": opt_scaling_k, + "opt_first_bin_size": opt_first_bin_size, + "history_dependence": self.history_dependence, + "firing_rate": firing_rate, + "recording_length": recording_length, + "H_spiking": H_spiking, + "max_R": max_Rs, + } + + if self.settings["analyse_auto_MI"]: + results["auto_MI"] = self.auto_MI.get("auto_MI") + results["auto_MI_delays"] = self.auto_MI.get("delay") results_d = DotDict(results) @@ -1097,10 +1275,10 @@

    Source code for idtxl.embedding_optimization_ais_Rudelt

    del self.max_R_T del self.process # del self.T_D - if self.settings['analyse_auto_MI']: + if self.settings["analyse_auto_MI"]: del self.auto_MI del self.H_spiking - if self.settings['estimation_method'] == 'bbc': + if self.settings["estimation_method"] == "bbc": del self.bbc_term
    @@ -1114,7 +1292,7 @@

    Source code for idtxl.embedding_optimization_ais_Rudelt

    Quick search
    @@ -1133,14 +1311,14 @@

    Navigation

  • modules |
  • - +
    \ No newline at end of file diff --git a/docs/html/_modules/idtxl/estimators_Rudelt.html b/docs/html/_modules/idtxl/estimators_Rudelt.html index 421ce0df..b352b7b9 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 documentation + idtxl.estimators_Rudelt — IDTxl 1.5.1 documentation @@ -31,7 +31,7 @@

    Navigation

  • modules |
  • - + @@ -60,11 +60,15 @@

    Source code for idtxl.estimators_Rudelt

         import idtxl.hde_fast_embedding as fast_emb
     except:
         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
    -    """, file=stderr, flush=True)
    +    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
    +    """,
    +        file=stderr,
    +        flush=True,
    +    )
     
     
     logger = logging.getLogger(__name__)
    @@ -109,9 +113,9 @@ 

    Source code for idtxl.estimators_Rudelt

             self.settings = settings.copy()
     
             # Get defaults for estimator settings
    -        self.settings.setdefault('normalize', True)
    -        self.settings.setdefault('embedding_step_size', 0.005)
    -        self.settings.setdefault('return_averaged_R', True)
    +        self.settings.setdefault("normalize", True)
    +        self.settings.setdefault("embedding_step_size", 0.005)
    +        self.settings.setdefault("return_averaged_R", True)
     
             # check settings
             self._check_input_settings()
    @@ -132,81 +136,93 @@ 

    Source code for idtxl.estimators_Rudelt

             if settings is None:
                 return {}
             elif type(settings) is not dict:
    -            raise TypeError('settings should be a dictionary.')
    +            raise TypeError("settings should be a dictionary.")
             else:
                 return settings
     
         def _check_input_settings(self):
    -
             # check that required settings are defined
    -        required_settings = ['normalize',
    -                             'embedding_step_size',
    -                             'return_averaged_R']
    +        required_settings = ["normalize", "embedding_step_size", "return_averaged_R"]
     
             # check if all settings are defined
             for required_setting in required_settings:
                 if not required_setting in self.settings:
    -                sys.exit("Error in settings file: {} is not defined. Aborting.".format(required_setting))
    -
    -        assert (isinstance(self.settings['normalize'], bool)), \
    -                                    "Error: setting 'normalize' needs to be boolean but is defined as {0}. " \
    -                                     "Aborting.".format(type(self.settings['normalize']))
    -
    -        assert (isinstance(self.settings['return_averaged_R'], bool)),\
    -                                    "Error: setting 'return_averaged_R' needs to be boolean but is " \
    -                                    "defined as {0}. Aborting.".format(type(self.settings['normalize']))
    -
    -        assert (isinstance(self.settings['embedding_step_size'], float)),\
    -                                    "Error: setting 'embedding_step_size' " \
    -                                    "needs to be float but is defined " \
    -                                    "as {0}. Aborting.".format(type(self.settings['embedding_step_size']))
    -
    -    def _check_estimator_inputs(self,
    -                                symbol_array,
    -                                past_symbol_array,
    -                                current_symbol_array,
    -                                bbc_tolerance):
    -        assert (isinstance(symbol_array, np.ndarray)), \
    -            "Error: symbol_array needs to be a numpy array but is defines as {0}." \
    +                sys.exit(
    +                    "Error in settings file: {} is not defined. Aborting.".format(
    +                        required_setting
    +                    )
    +                )
    +
    +        assert isinstance(self.settings["normalize"], bool), (
    +            "Error: setting 'normalize' needs to be boolean but is defined as {0}. "
    +            "Aborting.".format(type(self.settings["normalize"]))
    +        )
    +
    +        assert isinstance(self.settings["return_averaged_R"], bool), (
    +            "Error: setting 'return_averaged_R' needs to be boolean but is "
    +            "defined as {0}. Aborting.".format(type(self.settings["normalize"]))
    +        )
    +
    +        assert isinstance(self.settings["embedding_step_size"], float), (
    +            "Error: setting 'embedding_step_size' "
    +            "needs to be float but is defined "
    +            "as {0}. Aborting.".format(type(self.settings["embedding_step_size"]))
    +        )
    +
    +    def _check_estimator_inputs(
    +        self, symbol_array, past_symbol_array, current_symbol_array, bbc_tolerance
    +    ):
    +        assert isinstance(symbol_array, np.ndarray), (
    +            "Error: symbol_array needs to be a numpy array but is defines as {0}."
                 "Aborting.".format(type(symbol_array))
    +        )
     
             if past_symbol_array is not None:
    -            assert (isinstance(past_symbol_array, np.ndarray)), \
    -                "Error: past_symbol_array needs to be a numpy array but is defines as {0}." \
    +            assert isinstance(past_symbol_array, np.ndarray), (
    +                "Error: past_symbol_array needs to be a numpy array but is defines as {0}."
                     "Aborting.".format(type(past_symbol_array))
    -            assert (len(past_symbol_array) == len(symbol_array)), \
    -                "Error: symbol_array and past_symbol_array need to have the same length but have:" \
    -                "len(symbol_array): {0} len(past_symbol_array): {1}. " \
    -                "Aborting". format(len(symbol_array), len(past_symbol_array))
    +            )
    +            assert len(past_symbol_array) == len(symbol_array), (
    +                "Error: symbol_array and past_symbol_array need to have the same length but have:"
    +                "len(symbol_array): {0} len(past_symbol_array): {1}. "
    +                "Aborting".format(len(symbol_array), len(past_symbol_array))
    +            )
     
             if current_symbol_array is not None:
    -            assert (isinstance(current_symbol_array, np.ndarray)), \
    -                "Error: current_symbol_array needs to be a numpy array but is defines as {0}." \
    +            assert isinstance(current_symbol_array, np.ndarray), (
    +                "Error: current_symbol_array needs to be a numpy array but is defines as {0}."
                     "Aborting.".format(type(current_symbol_array))
    -            assert (len(current_symbol_array) == len(symbol_array)), \
    -                "Error: symbol_array and current_symbol_array need to have the same length but have:" \
    -                "len(symbol_array): {0} len(current_symbol_array): {1}. " \
    +            )
    +            assert len(current_symbol_array) == len(symbol_array), (
    +                "Error: symbol_array and current_symbol_array need to have the same length but have:"
    +                "len(symbol_array): {0} len(current_symbol_array): {1}. "
                     "Aborting".format(len(symbol_array), len(current_symbol_array))
    +            )
     
             if bbc_tolerance is not None:
    -            assert (isinstance(bbc_tolerance, np.ndarray)), \
    -                "Error: symbol array needs to be a numpy array but is defines as {0}." \
    +            assert isinstance(bbc_tolerance, np.ndarray), (
    +                "Error: symbol array needs to be a numpy array but is defines as {0}."
                     "Aborting.".format(type(current_symbol_array))
    +            )
     
         def _ensure_one_dim(self, var):
             """
             check if array is 1D
             """
             var = np.squeeze(var)
    -        assert (var.ndim == 1), "Input variable needs to be one dimensional. Aborting"
    +        assert var.ndim == 1, "Input variable needs to be one dimensional. Aborting"
     
     
    [docs] def get_past_range(self, number_of_bins_d, first_bin_size, scaling_k): """ Get the past range T of the embedding, based on the parameters d, tau_1 and k. """ - return np.sum([first_bin_size * 10 ** ((number_of_bins_d - i) * scaling_k) - for i in range(1, number_of_bins_d + 1)])
    + return sum( + [ + first_bin_size * 10 ** ((number_of_bins_d - i) * scaling_k) + for i in range(1, number_of_bins_d + 1) + ] + )
    [docs] def get_window_delimiters(self, number_of_bins_d, scaling_k, first_bin_size): """ @@ -217,11 +233,18 @@

    Source code for idtxl.estimators_Rudelt

             two consequent bins.
             """
     
    -        bin_sizes = [first_bin_size * 10 ** ((number_of_bins_d - i) * scaling_k)
    -                     for i in range(1, number_of_bins_d + 1)]
    -        window_delimiters = [sum([bin_sizes[j] for j in range(i)])
    -                             for i in range(1, number_of_bins_d + 1)]
    -        window_delimiters.append(window_delimiters[number_of_bins_d - 1] + self.settings['embedding_step_size'])
    +        bin_sizes = [
    +            first_bin_size * 10 ** ((number_of_bins_d - i) * scaling_k)
    +            for i in range(1, number_of_bins_d + 1)
    +        ]
    +        window_delimiters = [
    +            sum([bin_sizes[j] for j in range(i)])
    +            for i in range(1, number_of_bins_d + 1)
    +        ]
    +        window_delimiters.append(
    +            window_delimiters[number_of_bins_d - 1]
    +            + self.settings["embedding_step_size"]
    +        )
             return window_delimiters
    [docs] def get_median_number_of_spikes_per_bin(self, raw_symbols): @@ -274,13 +297,14 @@

    Source code for idtxl.estimators_Rudelt

             # as in get_median_number_of_spikes_per_bin, ie
             # including the response
     
    -        return sum([2 ** (number_of_bins_d - i - 1) * spikes_in_window[i]
    -                    for i in range(0, number_of_bins_d)])
    + return sum( + [ + 2 ** (number_of_bins_d - i - 1) * spikes_in_window[i] + for i in range(0, number_of_bins_d) + ] + )
    -
    [docs] def get_raw_symbols(self, - spike_times, - embedding, - first_bin_size): +
    [docs] def get_raw_symbols(self, spike_times, embedding, first_bin_size): """ Get the raw symbols (in which the number of spikes per bin are counted, ie not necessarily binary quantity), as obtained by applying the @@ -291,14 +315,16 @@

    Source code for idtxl.estimators_Rudelt

     
             # the window is the embedding plus the response,
             # ie the embedding and one additional bin of size embedding_step_size
    -        window_delimiters = self.get_window_delimiters(number_of_bins_d,
    -                                                       scaling_k,
    -                                                       first_bin_size)
    +        window_delimiters = self.get_window_delimiters(
    +            number_of_bins_d, scaling_k, first_bin_size
    +        )
             window_length = window_delimiters[-1]
             num_spike_times = len(spike_times)
             last_spike_time = spike_times[-1]
     
    -        num_symbols = int((last_spike_time - window_length) / self.settings['embedding_step_size'])
    +        num_symbols = int(
    +            (last_spike_time - window_length) / self.settings["embedding_step_size"]
    +        )
     
             raw_symbols = []
     
    @@ -306,24 +332,31 @@ 

    Source code for idtxl.estimators_Rudelt

             spike_index_lo = 0
     
             for symbol_num in range(num_symbols):
    -            while spike_index_lo < num_spike_times and spike_times[spike_index_lo] < time:
    +            while (
    +                spike_index_lo < num_spike_times and spike_times[spike_index_lo] < time
    +            ):
                     spike_index_lo += 1
                 spike_index_hi = spike_index_lo
    -            while (spike_index_hi < num_spike_times and
    -                   spike_times[spike_index_hi] < time + window_length):
    +            while (
    +                spike_index_hi < num_spike_times
    +                and spike_times[spike_index_hi] < time + window_length
    +            ):
                     spike_index_hi += 1
     
                 spikes_in_window = np.zeros(number_of_bins_d + 1)
     
                 embedding_bin_index = 0
                 for spike_index in range(spike_index_lo, spike_index_hi):
    -                while (spike_times[spike_index] > time + window_delimiters[embedding_bin_index]):
    +                while (
    +                    spike_times[spike_index]
    +                    > time + window_delimiters[embedding_bin_index]
    +                ):
                         embedding_bin_index += 1
                     spikes_in_window[embedding_bin_index] += 1
     
                 raw_symbols += [spikes_in_window]
     
    -            time += self.settings['embedding_step_size']
    +            time += self.settings["embedding_step_size"]
     
             return raw_symbols
    @@ -350,7 +383,9 @@

    Source code for idtxl.estimators_Rudelt

             """
     
             mk = dict(((value, 0) for value in symbol_counts.values()))
    -        number_of_observed_symbols = np.count_nonzero([value for value in symbol_counts.values()])
    +        number_of_observed_symbols = np.count_nonzero(
    +            [value for value in symbol_counts.values()]
    +        )
     
             for symbol in symbol_counts.keys():
                 mk[symbol_counts[symbol]] += 1
    @@ -407,21 +442,21 @@ 

    Source code for idtxl.estimators_Rudelt

             d_xi is the prior for the nsb estimator
             """
     
    -        return K * mp.psi(1, K * beta + 1.) - mp.psi(1, beta + 1.)
    + return K * mp.psi(1, K * beta + 1.0) - mp.psi(1, beta + 1.0)
    [docs] def d2_xi(self, beta, K): """ Second derivative of xi(beta) (cf d_xi). """ - return K ** 2 * mp.psi(2, K * beta + 1) - mp.psi(2, beta + 1)
    + return K**2 * mp.psi(2, K * beta + 1) - mp.psi(2, beta + 1)
    [docs] def d3_xi(self, beta, K): """ Third derivative of xi(beta) (cf d_xi). """ - return K ** 3 * mp.psi(3, K * beta + 1) - mp.psi(3, beta + 1)
    + return K**3 * mp.psi(3, K * beta + 1) - mp.psi(3, beta + 1)
    [docs] def rho(self, beta, mk, K, N): """ @@ -431,8 +466,9 @@

    Source code for idtxl.estimators_Rudelt

             the posterior for the nsb estimator
             """
     
    -        return np.prod([mp.power(mp.rf(beta, np.double(n)), mk[n]) for n in mk]) / mp.rf(K * beta,
    -                                                                                         np.double(N))
    + return np.prod( + [mp.power(mp.rf(beta, np.double(n)), mk[n]) for n in mk] + ) / mp.rf(K * beta, np.double(N))
    [docs] def unnormalized_posterior(self, beta, mk, K, N): """ @@ -449,16 +485,22 @@

    Source code for idtxl.estimators_Rudelt

             First derivate of the logarithm of the Dirichlet multinomial likelihood.
             """
     
    -        return K * (mp.psi(0, K * beta) - mp.psi(0, K * beta + N)) - K * mp.psi(0, beta) \
    -               + np.sum((mk[n] * mp.psi(0, n + beta) for n in mk))
    + return ( + K * (mp.psi(0, K * beta) - mp.psi(0, K * beta + N)) + - K * mp.psi(0, beta) + + sum((mk[n] * mp.psi(0, n + beta) for n in mk)) + )
    [docs] def d2_log_rho(self, beta, mk, K, N): """ Second derivate of the logarithm of the Dirichlet multinomial likelihood. """ - return K ** 2 * (mp.psi(1, K * beta) - mp.psi(1, K * beta + N)) - K * mp.psi(1, beta) \ - + np.sum((mk[n] * mp.psi(1, n + beta) for n in mk))
    + return ( + K**2 * (mp.psi(1, K * beta) - mp.psi(1, K * beta + N)) + - K * mp.psi(1, beta) + + sum((mk[n] * mp.psi(1, n + beta) for n in mk)) + )
    [docs] def d_log_rho_xi(self, beta, mk, K, N): """ @@ -472,15 +514,18 @@

    Source code for idtxl.estimators_Rudelt

             Second derivative of the logarithm of the nsb (unnormalized) posterior.
             """
     
    -        return self.d2_log_rho(beta, mk, K, N) \
    -               + (self.d3_xi(beta, K) * self.d_xi(beta, K) - self.d2_xi(beta, K) ** 2) / self.d_xi(beta, K) ** 2
    + return ( + self.d2_log_rho(beta, mk, K, N) + + (self.d3_xi(beta, K) * self.d_xi(beta, K) - self.d2_xi(beta, K) ** 2) + / self.d_xi(beta, K) ** 2 + )
    [docs] def log_likelihood_DP_alpha(self, a, K1, N): """ Alpha-dependent terms of the log-likelihood of a Dirichlet Process. """ - return (K1 - 1.) * mp.log(a) - mp.log(mp.rf(a + 1., N - 1.))
    + return (K1 - 1.0) * mp.log(a) - mp.log(mp.rf(a + 1.0, N - 1.0))
    [docs] def get_beta_MAP(self, mk, K, N): """ @@ -495,19 +540,27 @@

    Source code for idtxl.estimators_Rudelt

     
             K1 = K - mk[0]
     
    -        if self.d_log_rho(10 ** 1, mk, K, N) > 0:
    +        if self.d_log_rho(10**1, mk, K, N) > 0:
                 print("Warning: No ML parameter was found.", file=stderr, flush=True)
    -            beta_MAP = np.float('nan')
    +            beta_MAP = np.nan
             else:
                 try:
                     # first guess computed via posterior of Dirichlet process
                     DP_est = self.alpha_ML(mk, K1, N) / K
    -                beta_MAP = newton(lambda beta: float(self.d_log_rho_xi(beta, mk, K, N)), DP_est,
    -                                  lambda beta: float(self.d2_log_rho_xi(beta, mk, K, N)),
    -                                  tol=5e-08, maxiter=500)
    +                beta_MAP = newton(
    +                    lambda beta: float(self.d_log_rho_xi(beta, mk, K, N)),
    +                    DP_est,
    +                    lambda beta: float(self.d2_log_rho_xi(beta, mk, K, N)),
    +                    tol=5e-08,
    +                    maxiter=500,
    +                )
                 except:
    -                print("Warning: No ML parameter was found. (Exception caught.)", file=stderr, flush=True)
    -                beta_MAP = np.float('nan')
    +                print(
    +                    "Warning: No ML parameter was found. (Exception caught.)",
    +                    file=stderr,
    +                    flush=True,
    +                )
    +                beta_MAP = np.nan
             return beta_MAP
    [docs] def alpha_ML(self, mk, K1, N): @@ -518,14 +571,17 @@

    Source code for idtxl.estimators_Rudelt

     
             mk = utl.remove_key(mk, 0)
             # rnsum      = np.array([_logvarrhoi_DP(n, mk[n]) for n in mk]).sum()
    -        estlist = [N * (K1 - 1.) / r / (N - K1) for r in np.arange(6., 1.5, -0.5)]
    +        estlist = [N * (K1 - 1.0) / r / (N - K1) for r in np.arange(6.0, 1.5, -0.5)]
             varrholist = {}
             for a in estlist:
                 # varrholist[_logvarrho_DP(a, rnsum, K1, N)] = a
                 varrholist[self.log_likelihood_DP_alpha(a, K1, N)] = a
             a_est = varrholist[max(varrholist.keys())]
    -        res = minimize(lambda a: -self.log_likelihood_DP_alpha(a[0], K1, N),
    -                       a_est, method='Nelder-Mead')
    +        res = minimize(
    +            lambda a: -self.log_likelihood_DP_alpha(a[0], K1, N),
    +            a_est,
    +            method="Nelder-Mead",
    +        )
             return res.x[0]
    [docs] def get_integration_bounds(self, mk, K, N): @@ -539,11 +595,13 @@

    Source code for idtxl.estimators_Rudelt

     
             beta_MAP = self.get_beta_MAP(mk, K, N)
             if np.isnan(beta_MAP):
    -            intbounds = np.float('nan')
    +            intbounds = np.nan
             else:
    -            std = np.sqrt(- self.d2_log_rho_xi(beta_MAP, mk, K, N) ** (-1))
    -            intbounds = [np.float(np.amax([10 ** (-50), beta_MAP - 8 * std])),
    -                         np.float(beta_MAP + 8 * std)]
    +            std = np.sqrt(-self.d2_log_rho_xi(beta_MAP, mk, K, N) ** (-1))
    +            intbounds = [
    +                float(np.amax([10 ** (-50), beta_MAP - 8 * std])),
    +                float(beta_MAP + 8 * std),
    +            ]
     
             return intbounds
    @@ -556,8 +614,10 @@

    Source code for idtxl.estimators_Rudelt

             """
     
             norm = N + beta * K
    -        return mp.psi(0, norm + 1) - np.sum((mk[n] * (n + beta) *
    -                                             mp.psi(0, n + beta + 1) for n in mk)) / norm
    + return ( + mp.psi(0, norm + 1) + - sum((mk[n] * (n + beta) * mp.psi(0, n + beta + 1) for n in mk)) / norm + )
    [docs] def nsb_entropy(self, mk, K, N): """ @@ -585,25 +645,45 @@

    Source code for idtxl.estimators_Rudelt

                 def unnormalized_posterior_w(w, mk, K, N):
                     sbeta = w / (1 - w)
                     beta = sbeta * sbeta
    -                return self.unnormalized_posterior(beta, mk, K, N) * 2 * sbeta / (1 - w) / (1 - w)
    +                return (
    +                    self.unnormalized_posterior(beta, mk, K, N)
    +                    * 2
    +                    * sbeta
    +                    / (1 - w)
    +                    / (1 - w)
    +                )
     
                 def H1_w(w, mk, K, N):
                     sbeta = w / (1 - w)
                     beta = sbeta * sbeta
                     return self.H1(w, mk, K, N)
     
    -            marginal_likelihood = mp.quadgl(lambda w: unnormalized_posterior_w(w, mk, K, N),
    -                                            integration_bounds)
    -            H_nsb = mp.quadgl(lambda w: H1_w(w, mk, K, N) * unnormalized_posterior_w(w, mk, K, N),
    -                              integration_bounds) / marginal_likelihood
    +            marginal_likelihood = mp.quadgl(
    +                lambda w: unnormalized_posterior_w(w, mk, K, N), integration_bounds
    +            )
    +            H_nsb = (
    +                mp.quadgl(
    +                    lambda w: H1_w(w, mk, K, N) * unnormalized_posterior_w(w, mk, K, N),
    +                    integration_bounds,
    +                )
    +                / marginal_likelihood
    +            )
     
             else:
                 # integrate over the possible entropies, weighted such that every entropy is equally likely
                 # and normalize with the marginal likelihood
    -            marginal_likelihood = mp.quadgl(lambda beta: self.unnormalized_posterior(beta, mk, K, N),
    -                                            integration_bounds)
    -            H_nsb = mp.quadgl(lambda beta: self.H1(beta, mk, K, N) * self.unnormalized_posterior(beta, mk, K, N),
    -                              integration_bounds) / marginal_likelihood
    +            marginal_likelihood = mp.quadgl(
    +                lambda beta: self.unnormalized_posterior(beta, mk, K, N),
    +                integration_bounds,
    +            )
    +            H_nsb = (
    +                mp.quadgl(
    +                    lambda beta: self.H1(beta, mk, K, N)
    +                    * self.unnormalized_posterior(beta, mk, K, N),
    +                    integration_bounds,
    +                )
    +                / marginal_likelihood
    +            )
     
             return H_nsb
    @@ -635,12 +715,14 @@

    Source code for idtxl.estimators_Rudelt

             # Set default estimator settings.
             super().__init__(settings)
     
    -
    [docs] def nsb_estimator(self, - symbol_counts, - past_symbol_counts, - alphabet_size, - alphabet_size_past, - H_uncond): +
    [docs] def nsb_estimator( + self, + symbol_counts, + past_symbol_counts, + alphabet_size, + alphabet_size_past, + H_uncond, + ): """ Estimate the entropy of a system using the NSB estimator. """ @@ -680,10 +762,9 @@

    Source code for idtxl.estimators_Rudelt

                     MI / H_uncond (History dependence)
             """
     
    -        self._check_estimator_inputs(symbol_array,
    -                                     past_symbol_array,
    -                                     current_symbol_array,
    -                                     None)
    +        self._check_estimator_inputs(
    +            symbol_array, past_symbol_array, current_symbol_array, None
    +        )
     
             self._ensure_one_dim(symbol_array)
             self._ensure_one_dim(past_symbol_array)
    @@ -701,13 +782,15 @@ 

    Source code for idtxl.estimators_Rudelt

             alphabet_size_past = 2 ** int(number_of_bins_d_join - 1)  # K for past activity
             alphabet_size = alphabet_size_past * 2  # K
     
    -        I, R = self.nsb_estimator(symbol_counts,
    -                                  past_symbol_counts,
    -                                  alphabet_size,
    -                                  alphabet_size_past,
    -                                  H_uncond)
    +        I, R = self.nsb_estimator(
    +            symbol_counts,
    +            past_symbol_counts,
    +            alphabet_size,
    +            alphabet_size_past,
    +            H_uncond,
    +        )
     
    -        return np.float(I), np.float(R)
    + return float(I), float(R)
    [docs]class RudeltPluginEstimatorSymbolsMI(RudeltAbstractEstimator): @@ -739,14 +822,16 @@

    Source code for idtxl.estimators_Rudelt

             """
     
             mk = utl.remove_key(mk, 0)
    -        return - sum((mk[n] * (n / N) * np.log(n / N) for n in mk))
    - -
    [docs] def plugin_estimator(self, - symbol_counts, - past_symbol_counts, - alphabet_size, - alphabet_size_past, - H_uncond): + return -sum((mk[n] * (n / N) * np.log(n / N) for n in mk))
    + +
    [docs] def plugin_estimator( + self, + symbol_counts, + past_symbol_counts, + alphabet_size, + alphabet_size_past, + H_uncond, + ): """ Estimate the entropy of a system using the BBC estimator. """ @@ -786,10 +871,9 @@

    Source code for idtxl.estimators_Rudelt

                     MI / H_uncond (History dependence)
             """
     
    -        self._check_estimator_inputs(symbol_array,
    -                                     past_symbol_array,
    -                                     current_symbol_array,
    -                                     None)
    +        self._check_estimator_inputs(
    +            symbol_array, past_symbol_array, current_symbol_array, None
    +        )
     
             self._ensure_one_dim(symbol_array)
             self._ensure_one_dim(past_symbol_array)
    @@ -810,13 +894,15 @@ 

    Source code for idtxl.estimators_Rudelt

             alphabet_size_past = 2 ** int(number_of_bins_d_join - 1)  # K for past activity
             alphabet_size = alphabet_size_past * 2  # K
     
    -        I, R = self.plugin_estimator(symbol_counts,
    -                                     past_symbol_counts,
    -                                     alphabet_size,
    -                                     alphabet_size_past,
    -                                     H_uncond)
    +        I, R = self.plugin_estimator(
    +            symbol_counts,
    +            past_symbol_counts,
    +            alphabet_size,
    +            alphabet_size_past,
    +            H_uncond,
    +        )
     
    -        return np.float(I), np.float(R)
    + return float(I), float(R)
    [docs]class RudeltBBCEstimator(RudeltAbstractEstimator): @@ -872,7 +958,9 @@

    Source code for idtxl.estimators_Rudelt

             else:
                 return np.inf
    -
    [docs] def estimate(self, symbol_array, past_symbol_array, current_symbol_array, bbc_tolerance=None): +
    [docs] def estimate( + self, symbol_array, past_symbol_array, current_symbol_array, bbc_tolerance=None + ): """ Calculate the mutual information (MI) of one variable depending on its past using nsb and plugin estimator and check if bias criterion is passed/ @@ -898,29 +986,34 @@

    Source code for idtxl.estimators_Rudelt

                     criterion (bbc)
             """
     
    -        self._check_estimator_inputs(symbol_array,
    -                                     past_symbol_array,
    -                                     current_symbol_array,
    -                                     bbc_tolerance)
    +        self._check_estimator_inputs(
    +            symbol_array, past_symbol_array, current_symbol_array, bbc_tolerance
    +        )
     
             self._ensure_one_dim(symbol_array)
             self._ensure_one_dim(past_symbol_array)
             self._ensure_one_dim(current_symbol_array)
     
             estnsb = RudeltNSBEstimatorSymbolsMI()
    -        I_nsb, R_nsb = estnsb.estimate(symbol_array, past_symbol_array, current_symbol_array)
    +        I_nsb, R_nsb = estnsb.estimate(
    +            symbol_array, past_symbol_array, current_symbol_array
    +        )
     
             estplugin = RudeltPluginEstimatorSymbolsMI()
    -        I_plugin, R_plugin = estplugin.estimate(symbol_array, past_symbol_array, current_symbol_array)
    +        I_plugin, R_plugin = estplugin.estimate(
    +            symbol_array, past_symbol_array, current_symbol_array
    +        )
     
             if not bbc_tolerance == None:
                 if self.bayesian_bias_criterion(R_nsb, R_plugin, bbc_tolerance):
    -                return np.float(I_nsb), np.float(R_nsb)
    -            else:
    -                return None
    +                return float(I_nsb), float(R_nsb)
    +            return None
             else:
    -            return np.float(I_nsb), np.float(R_nsb), np.float(self.get_bbc_term(R_nsb,
    -                                                  R_plugin))
    + return ( + float(I_nsb), + float(R_nsb), + float(self.get_bbc_term(R_nsb, R_plugin)), + )
    [docs]class RudeltShufflingEstimator(RudeltAbstractEstimator): @@ -938,8 +1031,10 @@

    Source code for idtxl.estimators_Rudelt

             the plug-in estimator.
             """
     
    -        return [number_of_symbols[0] / sum(number_of_symbols),
    -                number_of_symbols[1] / sum(number_of_symbols)]
    + return [ + number_of_symbols[0] / sum(number_of_symbols), + number_of_symbols[1] / sum(number_of_symbols), + ]
    [docs] def get_P_X_past_uncond(self, past_symbol_counts, number_of_symbols): """ @@ -969,18 +1064,22 @@

    Source code for idtxl.estimators_Rudelt

             P_X_past_cond_X = [{}, {}]
             for response in [0, 1]:
                 for symbol in past_symbol_counts[response]:
    -                P_X_past_cond_X[response][symbol] \
    -                    = past_symbol_counts[response][symbol] / number_of_symbols[response]
    +                P_X_past_cond_X[response][symbol] = (
    +                    past_symbol_counts[response][symbol] / number_of_symbols[response]
    +                )
             return P_X_past_cond_X
    [docs] def get_H0_X_past_cond_X_eq_x(self, marginal_probabilities, number_of_bins_d): """ Compute H_0(X_past | X = x), cf get_H0_X_past_cond_X. """ - return utl.get_shannon_entropy(marginal_probabilities) \ - + utl.get_shannon_entropy(1 - marginal_probabilities)
    + return utl.get_shannon_entropy( + marginal_probabilities + ) + utl.get_shannon_entropy(1 - marginal_probabilities)
    -
    [docs] def get_H0_X_past_cond_X(self, marginal_probabilities, number_of_bins_d, P_X_uncond): +
    [docs] def get_H0_X_past_cond_X( + self, marginal_probabilities, number_of_bins_d, P_X_uncond + ): """ Compute H_0(X_past | X), the estimate of the entropy for the past symbols given a response, under the assumption that activity in @@ -988,10 +1087,15 @@

    Source code for idtxl.estimators_Rudelt

             """
             H0_X_past_cond_X_eq_x = [0, 0]
             for response in [0, 1]:
    -            H0_X_past_cond_X_eq_x[response] \
    -                = self.get_H0_X_past_cond_X_eq_x(marginal_probabilities[response],
    -                                            number_of_bins_d)
    -        return sum([P_X_uncond[response] * H0_X_past_cond_X_eq_x[response] for response in [0, 1]])
    + H0_X_past_cond_X_eq_x[response] = self.get_H0_X_past_cond_X_eq_x( + marginal_probabilities[response], number_of_bins_d + ) + return sum( + [ + P_X_uncond[response] * H0_X_past_cond_X_eq_x[response] + for response in [0, 1] + ] + )
    [docs] def get_H_X_past_uncond(self, P_X_past_uncond): """ @@ -1007,20 +1111,35 @@

    Source code for idtxl.estimators_Rudelt

             symbols, conditioned on the response X,  given their probabilities.
             """
     
    -        return sum((P_X_uncond[response] * self.get_H_X_past_uncond(P_X_past_cond_X[response])
    -                    for response in [0, 1]))
    - -
    [docs] def get_marginal_frequencies_of_spikes_in_bins(self, symbol_counts, number_of_bins_d): + return sum( + ( + P_X_uncond[response] + * self.get_H_X_past_uncond(P_X_past_cond_X[response]) + for response in [0, 1] + ) + )
    + +
    [docs] def get_marginal_frequencies_of_spikes_in_bins( + self, symbol_counts, number_of_bins_d + ): """ Compute for each past bin 1...d the sum of spikes found in that bin across all observed symbols. """ - return np.array(sum((self.symbol_binary_to_array(symbol, number_of_bins_d) - * symbol_counts[symbol] - for symbol in symbol_counts)), dtype=int)
    - -
    [docs] def get_shuffled_symbol_counts(self, symbol_counts, past_symbol_counts, number_of_bins_d, - number_of_symbols): + return np.array( + sum( + ( + self.symbol_binary_to_array(symbol, number_of_bins_d) + * symbol_counts[symbol] + for symbol in symbol_counts + ) + ), + dtype=int, + )
    + +
    [docs] def get_shuffled_symbol_counts( + self, symbol_counts, past_symbol_counts, number_of_bins_d, number_of_symbols + ): """ Simulate new data by, for each past bin 1...d, permutating the activity across all observed past_symbols (for a given response X). The marginal @@ -1029,22 +1148,37 @@

    Source code for idtxl.estimators_Rudelt

             """
             number_of_spikes = sum(past_symbol_counts[1].values())
     
    -        marginal_frequencies = [self.get_marginal_frequencies_of_spikes_in_bins(past_symbol_counts[response],
    -                                                                           number_of_bins_d)
    -                                for response in [0, 1]]
    +        marginal_frequencies = [
    +            self.get_marginal_frequencies_of_spikes_in_bins(
    +                past_symbol_counts[response], number_of_bins_d
    +            )
    +            for response in [0, 1]
    +        ]
     
    -        shuffled_past_symbols = [np.zeros(number_of_symbols[response]) for response in [0, 1]]
    +        shuffled_past_symbols = [
    +            np.zeros(number_of_symbols[response]) for response in [0, 1]
    +        ]
     
             for i in range(0, number_of_bins_d):
                 for response in [0, 1]:
    -                shuffled_past_symbols[response] \
    -                    += 2 ** (number_of_bins_d - i - 1) \
    -                       * np.random.permutation(np.hstack((np.ones(marginal_frequencies[response][i]),
    -                                                          np.zeros(number_of_symbols[response] \
    -                                                                   - marginal_frequencies[response][i]))))
    +                shuffled_past_symbols[response] += 2 ** (
    +                    number_of_bins_d - i - 1
    +                ) * np.random.permutation(
    +                    np.hstack(
    +                        (
    +                            np.ones(marginal_frequencies[response][i]),
    +                            np.zeros(
    +                                number_of_symbols[response]
    +                                - marginal_frequencies[response][i]
    +                            ),
    +                        )
    +                    )
    +                )
     
             for response in [0, 1]:
    -            shuffled_past_symbols[response] = np.array(shuffled_past_symbols[response], dtype=int)
    +            shuffled_past_symbols[response] = np.array(
    +                shuffled_past_symbols[response], dtype=int
    +            )
     
             shuffled_past_symbol_counts = [Counter(), Counter()]
     
    @@ -1052,8 +1186,10 @@ 

    Source code for idtxl.estimators_Rudelt

                 for past_symbol in shuffled_past_symbols[response]:
                     shuffled_past_symbol_counts[response][past_symbol] += 1
     
    -        marginal_probabilities = [marginal_frequencies[response] / number_of_symbols[response]
    -                                  for response in [0, 1]]
    +        marginal_probabilities = [
    +            marginal_frequencies[response] / number_of_symbols[response]
    +            for response in [0, 1]
    +        ]
     
             return shuffled_past_symbol_counts, marginal_probabilities
    @@ -1112,11 +1248,17 @@

    Source code for idtxl.estimators_Rudelt

     
             # plug-in estimate
             past_symbol_counts = utl.get_past_symbol_counts(symbol_counts, merge=False)
    -        number_of_symbols = [sum(past_symbol_counts[response].values()) for response in [0, 1]]
    +        number_of_symbols = [
    +            sum(past_symbol_counts[response].values()) for response in [0, 1]
    +        ]
     
             P_X_uncond = self.get_P_X_uncond(number_of_symbols)
    -        P_X_past_uncond = self.get_P_X_past_uncond(past_symbol_counts, number_of_symbols)
    -        P_X_past_cond_X = self.get_P_X_past_cond_X(past_symbol_counts, number_of_symbols)
    +        P_X_past_uncond = self.get_P_X_past_uncond(
    +            past_symbol_counts, number_of_symbols
    +        )
    +        P_X_past_cond_X = self.get_P_X_past_cond_X(
    +            past_symbol_counts, number_of_symbols
    +        )
     
             H_X_past_uncond = self.get_H_X_past_uncond(P_X_past_uncond)
             H_X_past_cond_X = self.get_H_X_past_cond_X(P_X_uncond, P_X_past_cond_X)
    @@ -1124,13 +1266,20 @@ 

    Source code for idtxl.estimators_Rudelt

             I_plugin = H_X_past_uncond - H_X_past_cond_X
     
             # correction term
    -        shuffled_past_symbol_counts, marginal_probabilities \
    -            = self.get_shuffled_symbol_counts(symbol_counts, past_symbol_counts, number_of_bins_d,
    -                                         number_of_symbols)
    -
    -        P0_sh_X_past_cond_X = self.get_P_X_past_cond_X(shuffled_past_symbol_counts, number_of_symbols)
    -
    -        H0_X_past_cond_X = self.get_H0_X_past_cond_X(marginal_probabilities, number_of_bins_d, P_X_uncond)
    +        (
    +            shuffled_past_symbol_counts,
    +            marginal_probabilities,
    +        ) = self.get_shuffled_symbol_counts(
    +            symbol_counts, past_symbol_counts, number_of_bins_d, number_of_symbols
    +        )
    +
    +        P0_sh_X_past_cond_X = self.get_P_X_past_cond_X(
    +            shuffled_past_symbol_counts, number_of_symbols
    +        )
    +
    +        H0_X_past_cond_X = self.get_H0_X_past_cond_X(
    +            marginal_probabilities, number_of_bins_d, P_X_uncond
    +        )
             H0_sh_X_past_cond_X = self.get_H_X_past_cond_X(P_X_uncond, P0_sh_X_past_cond_X)
     
             I_corr = H0_X_past_cond_X - H0_sh_X_past_cond_X
    @@ -1154,10 +1303,7 @@ 

    Source code for idtxl.estimators_Rudelt

                     MI / H_uncond (History dependence)
             """
     
    -        self._check_estimator_inputs(symbol_array,
    -                                     None,
    -                                     None,
    -                                     None)
    +        self._check_estimator_inputs(symbol_array, None, None, None)
     
             self._ensure_one_dim(symbol_array)
     
    @@ -1168,8 +1314,7 @@ 

    Source code for idtxl.estimators_Rudelt

     
             H_uncond = utl.get_H_spiking(symbol_counts)
     
    -        I_sh = self.shuffling_MI(symbol_counts,
    -                            number_of_bins_d_join -1)
    +        I_sh = self.shuffling_MI(symbol_counts, number_of_bins_d_join - 1)
     
             R_sh = I_sh / H_uncond
     
    @@ -1205,7 +1350,7 @@ 

    Navigation

  • modules |
  • - + diff --git a/docs/html/_modules/idtxl/estimators_jidt.html b/docs/html/_modules/idtxl/estimators_jidt.html index 7cf844b0..907b2e58 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 documentation + idtxl.estimators_jidt — IDTxl 1.5.1 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 4570af76..e6752b21 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 documentation + idtxl.estimators_mpi — IDTxl 1.5.1 documentation @@ -31,7 +31,7 @@

    Navigation

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

    Source code for idtxl.estimators_mpi

     try:
         from mpi4py.futures import MPIPoolExecutor
     except ImportError as err:
    -    ex.package_missing(err, 'MPI is not available on this system. Install it'
    -                       'from https://pypi.org/project/mpi4py/ to use'
    -                       'MPI parallelization.')
    -    raise err
    +    ex.package_missing(
    +        err,
    +        "MPI is not available on this system. Install it"
    +        "from https://pypi.org/project/mpi4py/ to use"
    +        "MPI parallelization.",
    +    )
     
     _worker_estimators = {}
     """Estimator instances on worker ranks
    @@ -75,7 +77,6 @@ 

    Source code for idtxl.estimators_mpi

     
         # Create new estimator if necessary
         if id_ not in _worker_estimators:
    -
             # There is currently no good way to delete Estimators from _worker_estimators
             # caches when the corresponding MPIEstimator ceases to exist.
             # To avoid memory leaks, we currently allow only a single cached estimator that is replaced for new MPIEstimators.
    @@ -102,20 +103,20 @@ 

    Source code for idtxl.estimators_mpi

     
    [docs]class MPIEstimator(Estimator): """MPI Wrapper for arbitrary Estimator implementations - Make sure to have an "if __name__=='__main__':" guard in your main script to avoid - infinite recursion! + Make sure to have an "if __name__=='__main__':" guard in your main script + to avoid infinite recursion! - To use MPI, add MPI=True to the Estimator settings dictionary and optionally provide max_workers + To use MPI, add MPI=True to the Estimator settings dictionary and + optionally provide max_workers Call using mpiexec: - mpiexec -n 1 -usize <max workers + 1> python <python script> + >>> mpiexec -n 1 -usize <max workers + 1> python <python script> or, if MPI does not support spawning new workers (i.e. MPI version < 2) - mpiexec -n <max workers + 1> python -m mpi4py.futures <python script> + >>> mpiexec -n <max workers + 1> python -m mpi4py.futures <python script> Call using slurm: - srun -n $SLURM_NTASKS --mpi=pmi2 python -m mpi4py.futures <python script> - + >>> srun -n $SLURM_NTASKS --mpi=pmi2 python -m mpi4py.futures <python script> """ def __init__(self, est, settings): @@ -124,9 +125,13 @@

    Source code for idtxl.estimators_mpi

             Immediately creates instances of est on each MPI worker.
     
             Args:
    -            est (str | Callable[[dict], Estimator]): Name of of or callable returning an instance of the base Estimator
    -            settings (dict): settings for the base Estimator.
    -                max_workers (optional): Number of MPI workers. Default: MPI_UNIVERSE_SIZE
    +            est : str | Callable[[dict], Estimator]
    +                Name of of or callable returning an instance of the base
    +                Estimator
    +            settings : dict
    +                settings for the base Estimator.
    +            max_workers : int (optional)
    +                Number of MPI workers, default=MPI_UNIVERSE_SIZE
             """
     
             self._est = est
    @@ -136,23 +141,22 @@ 

    Source code for idtxl.estimators_mpi

             self._id = uuid4().int
     
             # Create the MPIPoolExecutor and initialize Estimators on worker ranks
    -        self._executor = MPIPoolExecutor(
    -            max_workers=settings.get('max_workers', None))
    +        self._executor = MPIPoolExecutor(max_workers=settings.get("max_workers", None))
     
             # Boot up the executor with timeout
    -        with timeout(timeout_duration=settings.get('mpi_bootup_timeout', 10), exception_message='Bootup of MPI workers timed out.\n\
    -                Make sure the script was started in an MPI enrivonment using mpiexec, mpirun, srun (SLURM) or equivalent.\n\
    -                If necessary, increase the timeout in the settings dictionary using the key mpi_bootup_timeout.'):
    +        with timeout(
    +            timeout_duration=settings.get("mpi_bootup_timeout", 10),
    +            exception_message="Bootup of MPI workers timed out.\n\
    +                Make sure the script was started in an MPI enrivonment using mpiexec, mpirun, srun (SLURM) or equivalent.\n\
    +                If necessary, increase the timeout in the settings dictionary using the key mpi_bootup_timeout.",
    +        ):
                 self._executor.bootup(wait=True)
     
             # Create Estimator for rank 0.
             _get_worker_estimator(self._id, est, settings)
     
         def __del__(self):
    -        """
    -        Shut down MPIPoolExecutor upon deletion of MPIEstimator
    -        """
    -
    +        """Shut down MPIPoolExecutor upon deletion of MPIEstimator"""
             self._executor.shutdown()
     
         def _chunk_data(self, data, chunksize, n_chunks):
    @@ -160,37 +164,55 @@ 

    Source code for idtxl.estimators_mpi

             Iterator chopping data dictionary into n_chunks chunks of size chunksize
             """
             for i in range(n_chunks):
    -            yield {var: (None if data[var] is None else data[var][i*chunksize:(i+1)*chunksize]) for var in data}
    +            yield {
    +                var: (
    +                    None
    +                    if data[var] is None
    +                    else data[var][i * chunksize : (i + 1) * chunksize]
    +                )
    +                for var in data
    +            }
     
     
    [docs] def estimate(self, *, n_chunks=1, **data): """Distributes the given chunks of a task to Estimators on worker ranks using MPI. + Needs to be called with kwargs only. + Args: - n_chunks (int, optional): Number of chunks to split the data into. Defaults to 1. - data (dict[str, Sequence]): Dictionary of random variable realizations + n_chunks : int [optional] + Number of chunks to split the data into, default=1. + data : dict[str, Sequence] + Dictionary of random variable realizations Returns: - numpy array: Estimates of information-theoretic quantities as np.double values + numpy array + Estimates of information-theoretic quantities as np.double + values """ - assert n_chunks > 0, 'Number of chunks must be at least one.' + assert n_chunks > 0, "Number of chunks must be at least one." samplesize = len(next(iter(data.values()))) - assert all(var is None or len(var) == samplesize for var in data.values( - )), 'All variables must have the same number of realizations.' + assert all( + var is None or len(var) == samplesize for var in data.values() + ), "All variables must have the same number of realizations." - assert samplesize % n_chunks == 0, 'Number of realizations must be divisible by number of chunks!' + assert ( + samplesize % n_chunks == 0 + ), "Number of realizations must be divisible by number of chunks!" # Split the data into chunks chunksize = samplesize // n_chunks chunked_data = self._chunk_data(data, chunksize, n_chunks) - result_generator = self._executor.map(_dispatch_task, - itertools.repeat(self._id), - itertools.repeat(self._est), - itertools.repeat(self._settings), - chunked_data) + result_generator = self._executor.map( + _dispatch_task, + itertools.repeat(self._id), + itertools.repeat(self._est), + itertools.repeat(self._settings), + chunked_data, + ) return np.fromiter(result_generator, dtype=np.double)
    @@ -198,18 +220,22 @@

    Source code for idtxl.estimators_mpi

             return True
    [docs] def is_analytic_null_estimator(self): - """Test if the base Estimator is an analytic null estimator. + """Test if the base Estimator is an analytic null estimator.""" - """ - - return _get_worker_estimator(self._id, self._est, self._settings).is_analytic_null_estimator()
    + return _get_worker_estimator( + self._id, self._est, self._settings + ).is_analytic_null_estimator()
    [docs] def estimate_surrogates_analytic(self, **data): """Forward analytic estimation to the base Estimator. - Analytic estimation is assumed to have shorter runtime and is thus performed on rank 0 alone for now. + + Analytic estimation is assumed to have shorter runtime and is thus + performed on rank 0 alone for now. """ - return _get_worker_estimator(self._id, self._est, self._settings).estimate_surrogates_analytic(**data)
    + return _get_worker_estimator( + self._id, self._est, self._settings + ).estimate_surrogates_analytic(**data)
    @@ -241,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 2935844e..66407fcb 100644 --- a/docs/html/_modules/idtxl/estimators_multivariate_pid.html +++ b/docs/html/_modules/idtxl/estimators_multivariate_pid.html @@ -5,14 +5,13 @@ - idtxl.estimators_multivariate_pid — IDTxl 1.4 documentation - - - + idtxl.estimators_multivariate_pid — IDTxl 1.5.1 documentation + + + - @@ -32,7 +31,7 @@

    Navigation

  • modules |
  • - + @@ -234,7 +233,7 @@

    Source code for idtxl.estimators_multivariate_pid

    Quick search
    @@ -253,14 +252,14 @@

    Navigation

  • modules |
  • - +
    \ No newline at end of file diff --git a/docs/html/_modules/idtxl/estimators_opencl.html b/docs/html/_modules/idtxl/estimators_opencl.html index b7a015d3..e1d13da7 100644 --- a/docs/html/_modules/idtxl/estimators_opencl.html +++ b/docs/html/_modules/idtxl/estimators_opencl.html @@ -5,14 +5,13 @@ - idtxl.estimators_opencl — IDTxl 1.4 documentation - - - + idtxl.estimators_opencl — IDTxl 1.5.1 documentation + + + - @@ -32,7 +31,7 @@

    Navigation

  • modules |
  • - + @@ -871,7 +870,7 @@

    Source code for idtxl.estimators_opencl

       

    Quick search

    @@ -890,14 +889,14 @@

    Navigation

  • modules |
  • - +
    \ No newline at end of file diff --git a/docs/html/_modules/idtxl/estimators_pid.html b/docs/html/_modules/idtxl/estimators_pid.html index edc3ac36..45b51bb9 100644 --- a/docs/html/_modules/idtxl/estimators_pid.html +++ b/docs/html/_modules/idtxl/estimators_pid.html @@ -5,14 +5,13 @@ - idtxl.estimators_pid — IDTxl 1.4 documentation - - - + idtxl.estimators_pid — IDTxl 1.5.1 documentation + + + - @@ -32,7 +31,7 @@

    Navigation

  • modules |
  • - + @@ -117,43 +116,46 @@

    Source code for idtxl.estimators_pid

                 - verbose : bool [optional] - print output to console
                   (default=False)
         """
    +
         def __init__(self, settings):
             try:
    -            settings['alph_s1']
    +            settings["alph_s1"]
             except KeyError:
                 print('"alph_s1" is missing from the settings dictionary.')
                 raise
             try:
    -            settings['alph_s2']
    +            settings["alph_s2"]
             except KeyError:
                 print('"alph_s2" is missing from the settings dictionary.')
                 raise
             try:
    -            settings['alph_t']
    +            settings["alph_t"]
             except KeyError:
                 print('"alph_t" is missing from the settings dictionary.')
                 raise
             try:
    -            settings['max_unsuc_swaps_row_parm']
    +            settings["max_unsuc_swaps_row_parm"]
             except KeyError:
    -            print('"max_unsuc_swaps_row_parm" is missing from the settings '
    -                  'dictionary.')
    +            print(
    +                '"max_unsuc_swaps_row_parm" is missing from the settings ' "dictionary."
    +            )
                 raise
             try:
    -            settings['num_reps']
    +            settings["num_reps"]
             except KeyError:
                 print('"num_reps" is missing from the settings dictionary.')
                 raise
    -        if settings['num_reps'] > 63:
    -            raise ValueError('Number of reps must be 63 or less to prevent '
    -                             'integer overflow.')
    +        if settings["num_reps"] > 63:
    +            raise ValueError(
    +                "Number of reps must be 63 or less to prevent " "integer overflow."
    +            )
             try:
    -            settings['max_iters']
    +            settings["max_iters"]
             except KeyError:
                 print('"max_iters" is missing from the settings dictionary.')
                 raise
             self.settings = settings.copy()
    -        self.settings.setdefault('verbose', False)
    +        self.settings.setdefault("verbose", False)
     
     
    [docs] def is_parallel(): return False
    @@ -184,34 +186,34 @@

    Source code for idtxl.estimators_pid

             except AttributeError as err:
                 if "'module' object has no attribute 'longdouble'" == err.args[0]:
                     raise RuntimeError(
    -                        'This system doesn''t seem to support longdouble '
    -                        '(requirement for using the Sydney PID-estimator.')
    +                    "This system doesn"
    +                    "t seem to support longdouble "
    +                    "(requirement for using the Sydney PID-estimator."
    +                )
                 else:
                     raise
     
             # -- DEFINE PARAMETERS -- #
     
             num_samples = len(t)
    -        alph_t = self.settings['alph_t']
    -        alph_s1 = self.settings['alph_s1']
    -        alph_s2 = self.settings['alph_s2']
    -        max_unsuc_swaps_row_parm = self.settings['max_unsuc_swaps_row_parm']
    +        alph_t = self.settings["alph_t"]
    +        alph_s1 = self.settings["alph_s1"]
    +        alph_s2 = self.settings["alph_s2"]
    +        max_unsuc_swaps_row_parm = self.settings["max_unsuc_swaps_row_parm"]
             # Max swaps = number of possible swaps * control parameter
    -        num_pos_swaps = alph_t * alph_s1 * (alph_s1-1) * alph_s2 * (alph_s2-1)
    -        max_unsuc_swaps_row = np.floor(num_pos_swaps *
    -                                       max_unsuc_swaps_row_parm)
    +        num_pos_swaps = alph_t * alph_s1 * (alph_s1 - 1) * alph_s2 * (alph_s2 - 1)
    +        max_unsuc_swaps_row = np.floor(num_pos_swaps * max_unsuc_swaps_row_parm)
     
             # -- CALCULATE PROBABLITIES -- #
     
             # Declare arrays for counts
    -        t_count = np.zeros(alph_t, dtype=np.int)
    -        s1_count = np.zeros(alph_s1, dtype=np.int)
    -        s2_count = np.zeros(alph_s2, dtype=np.int)
    -        joint_t_s1_count = np.zeros((alph_t, alph_s1), dtype=np.int)
    -        joint_t_s2_count = np.zeros((alph_t, alph_s2), dtype=np.int)
    -        joint_s1_s2_count = np.zeros((alph_s1, alph_s2), dtype=np.int)
    -        joint_t_s1_s2_count = np.zeros((alph_t, alph_s1, alph_s2),
    -                                       dtype=np.int)
    +        t_count = np.zeros(alph_t, dtype=int)
    +        s1_count = np.zeros(alph_s1, dtype=int)
    +        s2_count = np.zeros(alph_s2, dtype=int)
    +        joint_t_s1_count = np.zeros((alph_t, alph_s1), dtype=int)
    +        joint_t_s2_count = np.zeros((alph_t, alph_s2), dtype=int)
    +        joint_s1_s2_count = np.zeros((alph_s1, alph_s2), dtype=int)
    +        joint_t_s1_s2_count = np.zeros((alph_t, alph_s1, alph_s2), dtype=int)
     
             # Count observations
             for obs in range(0, num_samples):
    @@ -228,43 +230,45 @@ 

    Source code for idtxl.estimators_pid

             #   			joint_t_s1_s2_count[np.nonzero(joint_t_s1_s2_count)])))
     
             max_joint_nonzero_count = np.max(
    -                        joint_t_s1_s2_count[np.nonzero(joint_t_s1_s2_count)])
    +            joint_t_s1_s2_count[np.nonzero(joint_t_s1_s2_count)]
    +        )
     
             # Fixed probabilities
    -        t_prob = np.divide(t_count, num_samples).astype('longdouble')
    -        s1_prob = np.divide(s1_count, num_samples).astype('longdouble')
    -        s2_prob = np.divide(s2_count, num_samples).astype('longdouble')
    -        joint_t_s1_prob = np.divide(joint_t_s1_count,
    -                                    num_samples).astype('longdouble')
    -        joint_t_s2_prob = np.divide(joint_t_s2_count,
    -                                    num_samples).astype('longdouble')
    +        t_prob = np.divide(t_count, num_samples).astype("longdouble")
    +        s1_prob = np.divide(s1_count, num_samples).astype("longdouble")
    +        s2_prob = np.divide(s2_count, num_samples).astype("longdouble")
    +        joint_t_s1_prob = np.divide(joint_t_s1_count, num_samples).astype("longdouble")
    +        joint_t_s2_prob = np.divide(joint_t_s2_count, num_samples).astype("longdouble")
     
             # Variable probabilities
    -        joint_s1_s2_prob = np.divide(joint_s1_s2_count,
    -                                     num_samples).astype('longdouble')
    -        joint_t_s1_s2_prob = np.divide(joint_t_s1_s2_count,
    -                                       num_samples).astype('longdouble')
    +        joint_s1_s2_prob = np.divide(joint_s1_s2_count, num_samples).astype(
    +            "longdouble"
    +        )
    +        joint_t_s1_s2_prob = np.divide(joint_t_s1_s2_count, num_samples).astype(
    +            "longdouble"
    +        )
             max_prob = np.max(joint_t_s1_s2_prob[np.nonzero(joint_t_s1_s2_prob)])
     
    -    #    # make copies of the variable probabilities for independent second
    -    #    # optimization and comparison of KLDs for convergence check:
    -    #    # KLDs should initially rise and then fall when close to the minimum
    -    #    joint_s1_s2_prob_alt = joint_s1_s2_prob.copy()
    -    #    joint_t_s1_s2_prob_alt = joint_t_s1_s2_prob.copy()
    +        #    # make copies of the variable probabilities for independent second
    +        #    # optimization and comparison of KLDs for convergence check:
    +        #    # KLDs should initially rise and then fall when close to the minimum
    +        #    joint_s1_s2_prob_alt = joint_s1_s2_prob.copy()
    +        #    joint_t_s1_s2_prob_alt = joint_t_s1_s2_prob.copy()
     
             # -- VIRTUALISED SWAPS -- #
     
             # Calculate the initial cmi's and store them
             cond_mut_info1 = self._cmi_prob(
    -            s2_prob, joint_t_s2_prob, joint_s1_s2_prob, joint_t_s1_s2_prob)
    +            s2_prob, joint_t_s2_prob, joint_s1_s2_prob, joint_t_s1_s2_prob
    +        )
             cur_cond_mut_info1 = cond_mut_info1
     
             joint_s2_s1_prob = np.transpose(joint_s1_s2_prob)
    -        joint_t_s2_s1_prob = np.ndarray.transpose(joint_t_s1_s2_prob,
    -                                                  [0, 2, 1])
    +        joint_t_s2_s1_prob = np.ndarray.transpose(joint_t_s1_s2_prob, [0, 2, 1])
     
             cond_mut_info2 = self._cmi_prob(
    -            s1_prob, joint_t_s1_prob, joint_s2_s1_prob, joint_t_s2_s1_prob)
    +            s1_prob, joint_t_s1_prob, joint_s2_s1_prob, joint_t_s2_s1_prob
    +        )
             cur_cond_mut_info2 = cond_mut_info2
     
             # sanity check: the curr cmi must be smaller than the joint, else
    @@ -272,11 +276,13 @@ 

    Source code for idtxl.estimators_pid

             jointmi_s1s2_t = self._joint_mi(s1, s2, t, alph_s1, alph_s2, alph_t)
     
             if cond_mut_info1 > jointmi_s1s2_t:
    -            raise ValueError('joint MI {0} smaller than cMI {1}'
    -                             ''.format(jointmi_s1s2_t, cond_mut_info1))
    +            raise ValueError(
    +                "joint MI {0} smaller than cMI {1}"
    +                "".format(jointmi_s1s2_t, cond_mut_info1)
    +            )
             else:
    -            if self.settings['verbose']:
    -                print('Passed sanity check on jMI and cMI')
    +            if self.settings["verbose"]:
    +                print("Passed sanity check on jMI and cMI")
     
             # Declare reps array of repeated doubling to half the prob_inc
             # WARNING: num_reps greater than 63 results in integer overflow
    @@ -290,42 +296,42 @@ 

    Source code for idtxl.estimators_pid

             # this does not run with the current code as it uses large powers of
             # integers another idea would be to decrement by something slightly
             # smaller than 2
    -    #    num_reps = num_reps + np.int32(np.floor(np.log(max_joint_nonzero_count)/np.log(2)))
    -        if self.settings['verbose']:
    -            print('num_reps: {0}'.format(self.settings['num_reps']))
    -        reps = np.array(np.power(2, range(0, self.settings['num_reps'])))
    +        #    num_reps = num_reps + np.int32(np.floor(np.log(max_joint_nonzero_count)/np.log(2)))
    +        if self.settings["verbose"]:
    +            print("num_reps: {0}".format(self.settings["num_reps"]))
    +        reps = np.array(np.power(2, range(0, self.settings["num_reps"])))
     
             # Replication loop
             for rep in reps:
                 prob_inc = np.multiply(
    -                np.longdouble(max_prob),
    -                np.divide(np.longdouble(1), np.longdouble(rep)))
    +                np.longdouble(max_prob), np.divide(np.longdouble(1), np.longdouble(rep))
    +            )
                 # Want to store number of succesive unsuccessful swaps
                 unsuccessful_swaps_row = 0
                 # SWAP LOOP
    -            for attempt_swap in range(0, self.settings['max_iters']):
    +            for attempt_swap in range(0, self.settings["max_iters"]):
                     # Pick a random candidate from the targets
                     t_cand = np.random.randint(0, alph_t)
                     s1_cand = np.random.randint(0, alph_s1)
                     s2_cand = np.random.randint(0, alph_s2)
     
                     # Pick a swap candidate
    -                s1_prim = np.random.randint(0, alph_s1-1)
    -                if (s1_prim >= s1_cand):
    +                s1_prim = np.random.randint(0, alph_s1 - 1)
    +                if s1_prim >= s1_cand:
                         s1_prim += 1
    -                s2_prim = np.random.randint(0, alph_s2-1)
    -                if (s2_prim >= s2_cand):
    +                s2_prim = np.random.randint(0, alph_s2 - 1)
    +                if s2_prim >= s2_cand:
                         s2_prim += 1
     
    -    #            unsuccessful_swaps_row = _try_swap(cur_cond_mut_info,
    -    #                                               joint_t_s1_s2_prob,
    -    #                                               joint_s1_s2_prob,
    -    #                                               joint_t_s2_prob, s2_prob,
    -    #                                               t_cand, s1_prim, s2_prim,
    -    #                                               s1_cand, s2_cand,
    -    #                                               prob_inc,
    -    #                                               unsuccessful_swaps_row)
    -    #            print("unsuccessful_swaps_row: {0}".format(unsuccessful_swaps_row))
    +                #            unsuccessful_swaps_row = _try_swap(cur_cond_mut_info,
    +                #                                               joint_t_s1_s2_prob,
    +                #                                               joint_s1_s2_prob,
    +                #                                               joint_t_s2_prob, s2_prob,
    +                #                                               t_cand, s1_prim, s2_prim,
    +                #                                               s1_cand, s2_cand,
    +                #                                               prob_inc,
    +                #                                               unsuccessful_swaps_row)
    +                #            print("unsuccessful_swaps_row: {0}".format(unsuccessful_swaps_row))
     
                     # START of a possible try_swap function
                     # based on a fixed set of candidates
    @@ -335,11 +341,12 @@ 

    Source code for idtxl.estimators_pid

                     # Ensure we can decrement without introducing neg probs
                     # this is very important as we start swaps in the size of the
                     # maximum probability
    -                if (joint_t_s1_s2_prob[t_cand, s1_cand, s2_cand] >= prob_inc and
    -                        joint_t_s1_s2_prob[t_cand, s1_prim, s2_prim] >= prob_inc and
    -                        joint_s1_s2_prob[s1_cand, s2_cand] >= prob_inc and
    -                        joint_s1_s2_prob[s1_prim, s2_prim] >= prob_inc):
    -
    +                if (
    +                    joint_t_s1_s2_prob[t_cand, s1_cand, s2_cand] >= prob_inc
    +                    and joint_t_s1_s2_prob[t_cand, s1_prim, s2_prim] >= prob_inc
    +                    and joint_s1_s2_prob[s1_cand, s2_cand] >= prob_inc
    +                    and joint_s1_s2_prob[s1_prim, s2_prim] >= prob_inc
    +                ):
                         joint_t_s1_s2_prob[t_cand, s1_cand, s2_cand] -= prob_inc
                         joint_t_s1_s2_prob[t_cand, s1_prim, s2_prim] -= prob_inc
                         joint_t_s1_s2_prob[t_cand, s1_cand, s2_prim] += prob_inc
    @@ -351,19 +358,19 @@ 

    Source code for idtxl.estimators_pid

                         joint_s1_s2_prob[s1_prim, s2_cand] += prob_inc
     
                         # Calculate the cmi after this virtual swap
    -                    cond_mut_info1 = self._cmi_prob(s2_prob,
    -                                                    joint_t_s2_prob,
    -                                                    joint_s1_s2_prob,
    -                                                    joint_t_s1_s2_prob)
    -                    cond_mut_info2 = self._cmi_prob(s2_prob,
    -                                                    joint_t_s2_prob,
    -                                                    joint_s1_s2_prob,
    -                                                    joint_t_s1_s2_prob)
    +                    cond_mut_info1 = self._cmi_prob(
    +                        s2_prob, joint_t_s2_prob, joint_s1_s2_prob, joint_t_s1_s2_prob
    +                    )
    +                    cond_mut_info2 = self._cmi_prob(
    +                        s2_prob, joint_t_s2_prob, joint_s1_s2_prob, joint_t_s1_s2_prob
    +                    )
     
                         # If at least one of the cmis is improved keep it,
                         # reset the unsuccessful swap counter
    -                    if (cond_mut_info1 < cur_cond_mut_info1 or
    -                            cond_mut_info2 < cur_cond_mut_info2):
    +                    if (
    +                        cond_mut_info1 < cur_cond_mut_info1
    +                        or cond_mut_info2 < cur_cond_mut_info2
    +                    ):
                             cur_cond_mut_info1 = cond_mut_info1
                             cur_cond_mut_info2 = cond_mut_info2
                             unsuccessful_swaps_row = 0
    @@ -385,7 +392,7 @@ 

    Source code for idtxl.estimators_pid

                         unsuccessful_swaps_row += 1
                     # END of a possible try_swap function
     
    -                if (unsuccessful_swaps_row >= max_unsuc_swaps_row):
    +                if unsuccessful_swaps_row >= max_unsuc_swaps_row:
                         break
     
             # print(cond_mut_info, '\t', prob_inc, '\t', unsuccessful_swaps_row)
    @@ -395,10 +402,9 @@ 

    Source code for idtxl.estimators_pid

             # Classical mutual information terms
             mi_target_s1 = self._mi_prob(t_prob, s1_prob, joint_t_s1_prob)
             mi_target_s2 = self._mi_prob(t_prob, s2_prob, joint_t_s2_prob)
    -        jointmi_s1s2_target = self._joint_mi(s1, s2, t, alph_s1, alph_s2,
    -                                             alph_t)
    -        if self.settings['verbose']:
    -            print('jointmi_s1s2_target: {0}'.format(jointmi_s1s2_target))
    +        jointmi_s1s2_target = self._joint_mi(s1, s2, t, alph_s1, alph_s2, alph_t)
    +        if self.settings["verbose"]:
    +            print("jointmi_s1s2_target: {0}".format(jointmi_s1s2_target))
     
             # PID terms
             unq_s1 = cond_mut_info1
    @@ -407,39 +413,46 @@ 

    Source code for idtxl.estimators_pid

             syn_s1_s2 = jointmi_s1s2_target - unq_s1 - unq_s2 - shd_s1_s2
     
             # Return scalars instead of 1-element numpy arrays
    -        return {'joint_mi_s1s2_t': jointmi_s1s2_target[0],
    -                'unq_s1': unq_s1[0],
    -                'unq_s2': unq_s2[0],
    -                'shd_s1_s2': shd_s1_s2[0],
    -                'syn_s1_s2': syn_s1_s2[0]}
    - - def _cmi_prob(self, s2cond_prob, joint_t_s2cond_prob, - joint_s1_s2cond_prob, joint_t_s1_s2cond_prob): - total = np.zeros(1).astype('longdouble') + return { + "joint_mi_s1s2_t": jointmi_s1s2_target[0], + "unq_s1": unq_s1[0], + "unq_s2": unq_s2[0], + "shd_s1_s2": shd_s1_s2[0], + "syn_s1_s2": syn_s1_s2[0], + }
    + + def _cmi_prob( + self, + s2cond_prob, + joint_t_s2cond_prob, + joint_s1_s2cond_prob, + joint_t_s1_s2cond_prob, + ): + total = np.zeros(1).astype("longdouble") [alph_t, alph_s1, alph_s2cond] = np.shape(joint_t_s1_s2cond_prob) for sym_s1 in range(0, alph_s1): for sym_s2cond in range(0, alph_s2cond): for sym_t in range(0, alph_t): - - if (s2cond_prob[sym_s2cond] * - joint_t_s2cond_prob[sym_t, sym_s2cond] * - joint_s1_s2cond_prob[sym_s1, sym_s2cond] * - joint_t_s1_s2cond_prob[sym_t, sym_s1, sym_s2cond] > - 0): - + if ( + s2cond_prob[sym_s2cond] + * joint_t_s2cond_prob[sym_t, sym_s2cond] + * joint_s1_s2cond_prob[sym_s1, sym_s2cond] + * joint_t_s1_s2cond_prob[sym_t, sym_s1, sym_s2cond] + > 0 + ): local_contrib = ( - np.log(joint_t_s1_s2cond_prob[sym_t, sym_s1, - sym_s2cond]) + - np.log(s2cond_prob[sym_s2cond]) - - np.log(joint_t_s2cond_prob[sym_t, sym_s2cond]) - - np.log(joint_s1_s2cond_prob[sym_s1, sym_s2cond]) - ) / np.log(2) + np.log(joint_t_s1_s2cond_prob[sym_t, sym_s1, sym_s2cond]) + + np.log(s2cond_prob[sym_s2cond]) + - np.log(joint_t_s2cond_prob[sym_t, sym_s2cond]) + - np.log(joint_s1_s2cond_prob[sym_s1, sym_s2cond]) + ) / np.log(2) weighted_contrib = ( - joint_t_s1_s2cond_prob[sym_t, sym_s1, sym_s2cond] * - local_contrib) + joint_t_s1_s2cond_prob[sym_t, sym_s1, sym_s2cond] + * local_contrib + ) else: weighted_contrib = 0 total += weighted_contrib @@ -448,23 +461,22 @@

    Source code for idtxl.estimators_pid

     
         def _mi_prob(self, s1_prob, s2_prob, joint_s1_s2_prob):
             """MI estimator in the prob domain."""
    -        total = np.zeros(1).astype('longdouble')
    +        total = np.zeros(1).astype("longdouble")
             [alph_s1, alph_s2] = np.shape(joint_s1_s2_prob)
     
             for sym_s1 in range(0, alph_s1):
                 for sym_s2 in range(0, alph_s2):
    -
    -                if (s1_prob[sym_s1] * s2_prob[sym_s2] *
    -                        joint_s1_s2_prob[sym_s1, sym_s2] > 0):
    -
    +                if (
    +                    s1_prob[sym_s1] * s2_prob[sym_s2] * joint_s1_s2_prob[sym_s1, sym_s2]
    +                    > 0
    +                ):
                         local_contrib = (
    -                        np.log(joint_s1_s2_prob[sym_s1, sym_s2]) -
    -                        np.log(s1_prob[sym_s1]) -
    -                        np.log(s2_prob[sym_s2])
    -                                    ) / np.log(2)
    +                        np.log(joint_s1_s2_prob[sym_s1, sym_s2])
    +                        - np.log(s1_prob[sym_s1])
    +                        - np.log(s2_prob[sym_s2])
    +                    ) / np.log(2)
     
    -                    weighted_contrib = (joint_s1_s2_prob[sym_s1, sym_s2] *
    -                                        local_contrib)
    +                    weighted_contrib = joint_s1_s2_prob[sym_s1, sym_s2] * local_contrib
                     else:
                         weighted_contrib = 0
                     total += weighted_contrib
    @@ -476,9 +488,9 @@ 

    Source code for idtxl.estimators_pid

     
             [s12, alph_s12] = _join_variables(s1, s2, alph_s1, alph_s2)
     
    -        t_count = np.zeros(alph_t, dtype=np.int)
    -        s12_count = np.zeros(alph_s12, dtype=np.int)
    -        joint_t_s12_count = np.zeros((alph_t, alph_s12), dtype=np.int)
    +        t_count = np.zeros(alph_t, dtype=int)
    +        s12_count = np.zeros(alph_s12, dtype=int)
    +        joint_t_s12_count = np.zeros((alph_t, alph_s12), dtype=int)
     
             num_samples = len(t)
     
    @@ -487,10 +499,11 @@ 

    Source code for idtxl.estimators_pid

                 s12_count[s12[obs]] += 1
                 joint_t_s12_count[t[obs], s12[obs]] += 1
     
    -        t_prob = np.divide(t_count, num_samples).astype('longdouble')
    -        s12_prob = np.divide(s12_count, num_samples).astype('longdouble')
    -        joint_t_s12_prob = np.divide(joint_t_s12_count,
    -                                     num_samples).astype('longdouble')
    +        t_prob = np.divide(t_count, num_samples).astype("longdouble")
    +        s12_prob = np.divide(s12_count, num_samples).astype("longdouble")
    +        joint_t_s12_prob = np.divide(joint_t_s12_count, num_samples).astype(
    +            "longdouble"
    +        )
     
             return self._mi_prob(t_prob, s12_prob, joint_t_s12_prob)
    @@ -542,9 +555,9 @@

    Source code for idtxl.estimators_pid

         def __init__(self, settings):
             # get estimation parameters
             self.settings = settings.copy()
    -        self.settings.setdefault('verbose', False)
    -        self.settings.setdefault('cone_solver', 'ECOS')
    -        self.settings.setdefault('solver_args', {'keep_solver_object': False})
    +        self.settings.setdefault("verbose", False)
    +        self.settings.setdefault("cone_solver", "ECOS")
    +        self.settings.setdefault("solver_args", {"keep_solver_object": False})
     
     
    [docs] def is_parallel(): return False
    @@ -569,18 +582,20 @@

    Source code for idtxl.estimators_pid

             s1, s2, t, self.settings = _check_input(s1, s2, t, self.settings)
             pdf = _get_pdf_dict(s1, s2, t)
     
    -        retval = synergy_tartu.pid(pdf_dirty=pdf,
    -                                   cone_solver=self.settings['cone_solver'],
    -                                   output=int(self.settings['verbose']),
    -                                   **self.settings['solver_args'])
    +        retval = synergy_tartu.pid(
    +            pdf_dirty=pdf,
    +            cone_solver=self.settings["cone_solver"],
    +            output=int(self.settings["verbose"]),
    +            **self.settings["solver_args"]
    +        )
     
             results = {
    -            'num_err': retval['Num_err'],
    -            'solver': retval['Solver'],
    -            'shd_s1_s2': retval['SI'],
    -            'syn_s1_s2': retval['CI'],
    -            'unq_s1': retval['UIY'],
    -            'unq_s2': retval['UIZ'],
    +            "num_err": retval["Num_err"],
    +            "solver": retval["Solver"],
    +            "shd_s1_s2": retval["SI"],
    +            "syn_s1_s2": retval["CI"],
    +            "unq_s1": retval["UIY"],
    +            "unq_s2": retval["UIZ"],
             }
             return results
    @@ -607,9 +622,8 @@

    Source code for idtxl.estimators_pid

     def _check_input(s1, s2, t, settings):
         """Check input to PID estimators."""
         # Check if inputs are numpy arrays.
    -    if (type(s1) != np.ndarray or type(s2) != np.ndarray or
    -            type(t) != np.ndarray):
    -        raise TypeError('All inputs, s1, s2, t, must be numpy arrays.')
    +    if type(s1) != np.ndarray or type(s2) != np.ndarray or type(t) != np.ndarray:
    +        raise TypeError("All inputs, s1, s2, t, must be numpy arrays.")
     
         # 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
    @@ -624,12 +638,12 @@ 

    Source code for idtxl.estimators_pid

                 alph_new = len(np.unique(s1[:, 0]))
                 for col in range(1, s1.shape[1]):
                     alph_col = len(np.unique(s1[:, col]))
    -                s1_joint, alph_new = _join_variables(s1_joint, s1[:, col],
    -                                                     alph_new, alph_col)
    -            settings['alph_s1'] = alph_new
    +                s1_joint, alph_new = _join_variables(
    +                    s1_joint, s1[:, col], alph_new, alph_col
    +                )
    +            settings["alph_s1"] = alph_new
             else:
    -            raise ValueError('Input source 1 s1 has to be a 1D or 2D numpy '
    -                             'array.')
    +            raise ValueError("Input source 1 s1 has to be a 1D or 2D numpy " "array.")
     
         if s2.ndim != 1:
             if s2.shape[1] == 1:
    @@ -639,36 +653,34 @@ 

    Source code for idtxl.estimators_pid

                 alph_new = len(np.unique(s2[:, 0]))
                 for col in range(1, s2.shape[1]):
                     alph_col = len(np.unique(s2[:, col]))
    -                s2_joint, alph_new = _join_variables(s2_joint, s2[:, col],
    -                                                     alph_new, alph_col)
    -            settings['alph_s2'] = alph_new
    +                s2_joint, alph_new = _join_variables(
    +                    s2_joint, s2[:, col], alph_new, alph_col
    +                )
    +            settings["alph_s2"] = alph_new
             else:
    -            raise ValueError('Input source 2 s2 has to be a 1D or 2D numpy '
    -                             'array.')
    +            raise ValueError("Input source 2 s2 has to be a 1D or 2D numpy " "array.")
         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.")
     
         if not issubclass(s1.dtype.type, np.integer):
    -        raise TypeError('Input s1 (source 1) must be an integer numpy array.')
    +        raise TypeError("Input s1 (source 1) must be an integer numpy array.")
         if not issubclass(s2.dtype.type, np.integer):
    -        raise TypeError('Input s2 (source 2) must be an integer numpy array.')
    +        raise TypeError("Input s2 (source 2) must be an integer numpy array.")
         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.
    -    if (len(t) != len(s1) or len(t) != len(s2)):
    -        raise ValueError('Number of samples s1, s2 and t must be equal')
    +    if len(t) != len(s1) or len(t) != len(s2):
    +        raise ValueError("Number of samples s1, s2 and t must be equal")
     
         return s1, s2, t, settings
    -
     
    @@ -681,7 +693,7 @@

    Source code for idtxl.estimators_pid

       

    Quick search

    @@ -700,14 +712,14 @@

    Navigation

  • modules |
  • - +
    \ No newline at end of file diff --git a/docs/html/_modules/idtxl/estimators_python.html b/docs/html/_modules/idtxl/estimators_python.html index c9eedf6e..ef7d8042 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 documentation + idtxl.estimators_python — IDTxl 1.5.1 documentation @@ -31,7 +31,7 @@

    Navigation

  • modules |
  • - + @@ -50,40 +50,62 @@

    Source code for idtxl.estimators_python

     from idtxl.estimator import Estimator
     from idtxl.knn.knn_finder_factory import get_knn_finder
     
    +
     
    [docs]class PythonKraskovCMI(Estimator): """Estimate conditional mutual information using Kraskov's first estimator. + + Args: + settings : dict [optional] + set estimator parameters: + + - kraskov_k : int [optional] - no. nearest neighbours for KNN + search (default=4) + - base : float - base of returned values (default=np=e) + - normalise : bool [optional] - z-standardise data (default=False) + - noise_level : float [optional] - random noise added to the data + (default=1e-8) + - rng_seed : int | None [optional] - random seed if noise level > 0 + - num_threads : int | str [optional] - number of threads used for + estimation (default='USE_ALL', note that this uses *all* + available threads on the current machine) + - knn_finder : str [optional] - knn algorithm to use, can be + 'scipy_kdtree' (default), 'sklearn_kdtree', or 'sklearn_balltree' """ def __init__(self, settings): - """Initialise estimator with settings. - """ + """Initialise estimator with settings.""" # Check for currently unsupported settings - if 'local_values' in settings or 'theiler_t' in settings or 'algorithm_num' in settings: - raise ValueError('This estimator currently does not support local_values, theiler_t or algorithm_num arguments.') + if ( + "local_values" in settings + or "theiler_t" in settings + or "algorithm_num" in settings + ): + raise ValueError( + "This estimator currently does not support local_values, theiler_t or algorithm_num arguments." + ) - self._knn_finder_settings = settings.get('knn_finder_settings', {}) + self._knn_finder_settings = settings.get("knn_finder_settings", {}) - self._kraskov_k = settings.get('kraskov_k', 4) - self._base = settings.get('base', np.e) - self._normalise = settings.get('normalise', False) + self._kraskov_k = settings.get("kraskov_k", 4) + self._base = settings.get("base", np.e) + self._normalise = settings.get("normalise", False) # Set number of threads - num_threads = settings.get('num_threads', -1) - if num_threads == 'USE_ALL': + num_threads = settings.get("num_threads", -1) + if num_threads == "USE_ALL": num_threads = -1 - self._knn_finder_settings['num_threads'] = num_threads + self._knn_finder_settings["num_threads"] = num_threads # Init rng for added gaussian noise - self._noise_level = settings.get('noise_level', 1e-8) + self._noise_level = settings.get("noise_level", 1e-8) if self._noise_level > 0: - rng_seed = settings.get('rng_seed', None) + rng_seed = settings.get("rng_seed", None) self._rng = np.random.default_rng(rng_seed) # Get KNN finder class - self._knn_finder_name = settings.get('knn_finder', 'scipy_kdtree') + self._knn_finder_name = settings.get("knn_finder", "scipy_kdtree") self._knn_finder_class = get_knn_finder(self._knn_finder_name) -
    [docs] def estimate(self, var1: np.ndarray, var2: np.ndarray, conditional=None): """Estimate conditional mutual information between var1 and var2, given @@ -93,19 +115,20 @@

    Source code for idtxl.estimators_python

             if conditional is None:
                 conditional = np.empty((len(var1), 0))
     
    -
             # Check the input data
             var1 = self._ensure_two_dim_input(var1)
             var2 = self._ensure_two_dim_input(var2)
             conditional = self._ensure_two_dim_input(conditional)
     
    -        assert var1.shape[0] == var2.shape[0] == conditional.shape[0], \
    -            f'Unequal number of observations (var1: {var1.shape[0]}, var2: {var2.shape[0]}, conditional: {conditional.shape[0]})'
    -        
    +        assert (
    +            var1.shape[0] == var2.shape[0] == conditional.shape[0]
    +        ), f"Unequal number of observations (var1: {var1.shape[0]}, var2: {var2.shape[0]}, conditional: {conditional.shape[0]})"
     
             # Check if number of points is sufficient for estimation.
             if var1.shape[0] - 1 < self._kraskov_k:
    -            raise ValueError(f'Not enough observations for Kraskov estimator (need at least {self._kraskov_k + 1}, got {var1.shape[0]}).')
    +            raise ValueError(
    +                f"Not enough observations for Kraskov estimator (need at least {self._kraskov_k + 1}, got {var1.shape[0]})."
    +            )
     
             # Normalise data
             if self._normalise:
    @@ -118,10 +141,14 @@ 

    Source code for idtxl.estimators_python

             if self._noise_level > 0:
                 var1 = var1 + self._rng.normal(0, self._noise_level, var1.shape)
                 var2 = var2 + self._rng.normal(0, self._noise_level, var2.shape)
    -            conditional = conditional + self._rng.normal(0, self._noise_level, conditional.shape)
    +            conditional = conditional + self._rng.normal(
    +                0, self._noise_level, conditional.shape
    +            )
     
             # Compute distances to kth nearest neighbors in the joint space
    -        epsilon = self._compute_epsilon(np.concatenate((var1, var2, conditional), axis=1), self._kraskov_k)
    +        epsilon = self._compute_epsilon(
    +            np.concatenate((var1, var2, conditional), axis=1), self._kraskov_k
    +        )
     
             # Count neighbors in the conditional space
             if conditional.shape[1] > 0:
    @@ -139,38 +166,42 @@ 

    Source code for idtxl.estimators_python

     
             if conditional.shape[1] > 0:
                 # Compute CMI
    -            return (digamma(self._kraskov_k)
    -                 + mean_digamma_nc
    -                 - mean_digamma_nc_var1
    -                 - mean_digamma_nc_var2
    -                ) / np.log(self._base)
    +            return (
    +                digamma(self._kraskov_k)
    +                + mean_digamma_nc
    +                - mean_digamma_nc_var1
    +                - mean_digamma_nc_var2
    +            ) / np.log(self._base)
             else:
                 # Compute MI
    -            return (digamma(self._kraskov_k)
    -                 + digamma(len(var1))
    -                 - mean_digamma_nc_var1
    -                 - mean_digamma_nc_var2
    -                ) / np.log(self._base)
    - + return ( + digamma(self._kraskov_k) + + digamma(len(var1)) + - mean_digamma_nc_var1 + - mean_digamma_nc_var2 + ) / np.log(self._base)
    + def _normalise_data(self, data: np.ndarray): """Standardise data to zero mean and unit variance.""" return (data - np.mean(data, axis=0)) / np.std(data, axis=0) - + def _compute_epsilon(self, data: np.ndarray, k: int): """Compute the distance to the kth nearest neighbor for each point in x.""" knn_finder = self._knn_finder_class(data, **self._knn_finder_settings) - return knn_finder.find_dist_to_kth_neighbor(data, k + 1) # +1 because the point itself is included in the data - + return knn_finder.find_dist_to_kth_neighbor( + data, k + 1 + ) # +1 because the point itself is included in the data + def _compute_n(self, data: np.ndarray, r: np.ndarray): """Count the number of neighbors strictly within a given radius r for each point in x. Returns the number of neighbors plus one, because the point itself is included in the data. """ knn_finder = self._knn_finder_class(data, **self._knn_finder_settings) return knn_finder.count_neighbors(data, r) - +
    [docs] def is_analytic_null_estimator(self): return False
    - +
    [docs] def is_parallel(self): return False
    @@ -204,7 +235,7 @@

    Navigation

  • modules |
  • - + diff --git a/docs/html/_modules/idtxl/idtxl_exceptions.html b/docs/html/_modules/idtxl/idtxl_exceptions.html index 17100703..fab56512 100644 --- a/docs/html/_modules/idtxl/idtxl_exceptions.html +++ b/docs/html/_modules/idtxl/idtxl_exceptions.html @@ -5,14 +5,13 @@ - idtxl.idtxl_exceptions — IDTxl 1.4 documentation - - - + idtxl.idtxl_exceptions — IDTxl 1.5.1 documentation + + + - @@ -32,7 +31,7 @@

    Navigation

  • modules |
  • - + @@ -61,10 +60,11 @@

    Source code for idtxl.idtxl_exceptions

         """Exception raised to signal that the estimators can no longer be used
         for this particular target (e.g. because of memory errors in high
         dimensions) but that the estimation could continue for others.
    -    
    +
         Attributes:
             message -- explanation of the error
         """
    +
         def __init__(self, message):
             self.message = message
    @@ -72,11 +72,11 @@

    Source code for idtxl.idtxl_exceptions

     
    [docs]class JidtOutOfMemoryError(AlgorithmExhaustedError): """Exception raised to signal a Java OutOfMemoryException. It is a child class of AlgorithmExhaustedError. - + Attributes: message -- explanation of the error """ - + def __init__(self, message): super().__init__(message)
    @@ -95,7 +95,7 @@

    Source code for idtxl.idtxl_exceptions

       

    Quick search

    @@ -114,14 +114,14 @@

    Navigation

  • modules |
  • - +
    \ No newline at end of file diff --git a/docs/html/_modules/idtxl/idtxl_io.html b/docs/html/_modules/idtxl/idtxl_io.html index 18ee79a2..6883e51b 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 documentation + idtxl.idtxl_io — IDTxl 1.5.1 documentation @@ -31,7 +31,7 @@

    Navigation

  • modules |
  • - + @@ -59,14 +59,18 @@

    Source code for idtxl.idtxl_io

     from scipy.io import loadmat
     from .data import Data
     from . import idtxl_exceptions as ex
    +
     try:
         import networkx as nx
     except ImportError as err:
         ex.package_missing(
             err,
    -        ('networkx is not available on this system. Install it from '
    -         'https://pypi.python.org/pypi/networkx/2.0 to export and plot IDTxl '
    -         'results in this format.'))
    +        (
    +            "networkx is not available on this system. Install it from "
    +            "https://pypi.python.org/pypi/networkx/2.0 to export and plot IDTxl "
    +            "results in this format."
    +        ),
    +    )
     
     DEBUG = False
     
    @@ -86,7 +90,7 @@ 

    Source code for idtxl.idtxl_io

         basic Python data types first.
         """
         data_json = _remove_numpy(d)
    -    with open(file_path, 'w') as outfile:
    +    with open(file_path, "w") as outfile:
             json.dump(obj=data_json, fp=outfile, sort_keys=True)
    @@ -120,7 +124,7 @@

    Source code for idtxl.idtxl_io

         data_json = cp.copy(data)
         for k in data_json.keys():
             if DEBUG:
    -            print('{0}, type: {1}'.format(data_json[k], type(data_json[k])))
    +            print("{0}, type: {1}".format(data_json[k], type(data_json[k])))
             if type(data_json[k]) is np.ndarray:
                 data_json[k] = data_json[k].tolist()
         return data_json
    @@ -178,7 +182,6 @@ 

    Source code for idtxl.idtxl_io

     #         return d
     
     
    -
     
    [docs]def save_pickle(obj, name): """Save objects using Python's pickle module. @@ -186,13 +189,13 @@

    Source code for idtxl.idtxl_io

             pickle.HIGHEST_PROTOCOL is a binary format, which may be inconvenient,
             but is good for performance. Protocol 0 is a text format.
         """
    -    with open(name, 'wb') as f:
    +    with open(name, "wb") as f:
             pickle.dump(obj, f, pickle.HIGHEST_PROTOCOL)
    [docs]def load_pickle(name): """Load objects that have been saved using Python's pickle module.""" - with open(name, 'rb') as f: + with open(name, "rb") as f: return pickle.load(f)
    @@ -233,19 +236,22 @@

    Source code for idtxl.idtxl_io

                 sampling rate, corresponding to the 'fsample' field
         """
         if file_version != "v7.3":
    -        raise RuntimeError('At present only m-files in format 7.3 are '
    -                           'supported, please consider reopening and resaving '
    -                           'your m-file in that version.')
    +        raise RuntimeError(
    +            "At present only m-files in format 7.3 are "
    +            "supported, please consider reopening and resaving "
    +            "your m-file in that version."
    +        )
             # TODO we could write a fallback option using numpy's loadmat?
     
    -    print('Creating Python dictionary from FT data structure: {0}'
    -          .format(ft_struct_name))
    +    print(
    +        "Creating Python dictionary from FT data structure: {0}".format(ft_struct_name)
    +    )
         trial_data = _ft_import_trial(file_name, ft_struct_name)
         label = _ft_import_label(file_name, ft_struct_name)
         fsample = _ft_fsample_2_float(file_name, ft_struct_name)
         timestamps = _ft_import_time(file_name, ft_struct_name)
     
    -    data = Data(data=trial_data, dim_order='spr', normalise=normalise)
    +    data = Data(data=trial_data, dim_order="spr", normalise=normalise)
         return data, label, timestamps, fsample
    @@ -257,18 +263,21 @@

    Source code for idtxl.idtxl_io

         # Get the trial cells that contain the references (pointers) to the data
         # we need. Then get the data from matrices in cells of a 1 x numtrials cell
         # array in the original FieldTrip structure.
    -    trial = ft_struct['trial']
    +    trial = ft_struct["trial"]
     
         # Get the trial cells that contain the references (pointers) to the data
         # we need. Then get the data from matrices in cells of a 1 x numtrials cell
         # array in the original FieldTrip structure.
    -    trial = ft_struct['trial']
    +    trial = ft_struct["trial"]
     
         # Allocate memory to hold actual data, read shape of first trial to know
         # the data size.
         trial_data_tmp = np.array(ft_file[trial[0][0]])  # get data from 1st trial
    -    print('Found data with first dimension: {0}, and second: {1}'
    -          .format(trial_data_tmp.shape[0], trial_data_tmp.shape[1]))
    +    print(
    +        "Found data with first dimension: {0}, and second: {1}".format(
    +            trial_data_tmp.shape[0], trial_data_tmp.shape[1]
    +        )
    +    )
         geometry = trial_data_tmp.shape + (trial.shape[0],)
         trial_data = np.empty(geometry)
     
    @@ -286,10 +295,10 @@ 

    Source code for idtxl.idtxl_io

         # for details of the data handling see comments in _ft_import_trial
         ft_file = h5py.File(file_name)
         ft_struct = ft_file[ft_struct_name]
    -    ft_label = ft_struct['label']
    +    ft_label = ft_struct["label"]
     
         if DEBUG:
    -        print('Converting FT labels to python list of strings')
    +        print("Converting FT labels to python list of strings")
     
         label = []
         for ll in range(0, ft_label.shape[0]):
    @@ -311,9 +320,9 @@ 

    Source code for idtxl.idtxl_io

         # for details of the data handling see comments in ft_trial_2_numpyarray
         ft_file = h5py.File(file_name)
         ft_struct = ft_file[ft_struct_name]
    -    ft_time = ft_struct['time']
    +    ft_time = ft_struct["time"]
         if DEBUG:
    -        print('Converting FT time cell array to numpy array')
    +        print("Converting FT time cell array to numpy array")
     
         np_timeaxis_tmp = np.array(ft_file[ft_time[0][0]])
         geometry = np_timeaxis_tmp.shape + (ft_time.shape[0],)
    @@ -328,15 +337,14 @@ 

    Source code for idtxl.idtxl_io

     def _ft_fsample_2_float(file_name, ft_struct_name):
         ft_file = h5py.File(file_name)
         ft_struct = ft_file[ft_struct_name]
    -    FTfsample = ft_struct['fsample']
    +    FTfsample = ft_struct["fsample"]
         fsample = int(FTfsample[0])
         if DEBUG:
    -        print('Converting FT fsample array (1x1) to numpy array (1x1)')
    +        print("Converting FT fsample array (1x1) to numpy array (1x1)")
         return fsample
     
     
    -
    [docs]def import_matarray(file_name, array_name, file_version, dim_order, - normalise=True): +
    [docs]def import_matarray(file_name, array_name, file_version, dim_order, normalise=True): """Read Matlab hdf5 file into IDTxl. reads a matlab hdf5 file ("-v7.3' or higher, .mat) or non-hdf5 files with a @@ -370,32 +378,37 @@

    Source code for idtxl.idtxl_io

                 instance of IDTxl Data object, containing data from the 'trial'
                 field
         """
    -    if file_version == 'v7.3':
    +    if file_version == "v7.3":
             mat_file = h5py.File(file_name)
             # Assert that at least one of the keys found at the top level of the
             # HDF file  matches the name of the array we wanted
             if array_name not in mat_file.keys():
    -            raise RuntimeError('Array {0} not in mat file or not a variable '
    -                               'at the file''s top level.'.format(array_name))
    +            raise RuntimeError(
    +                "Array {0} not in mat file or not a variable "
    +                "at the file"
    +                "s top level.".format(array_name)
    +            )
     
             # 2. Create an object for the matlab array (from the hdf5 hierachy),
             # the trailing [()] ensures everything is read
             mat_data = np.squeeze(np.asarray(mat_file[array_name][()]))
     
    -    elif file_version in ['v4', 'v6', 'v7']:
    +    elif file_version in ["v4", "v6", "v7"]:
             try:
                 m = loadmat(file_name, squeeze_me=True, variable_names=array_name)
             except NotImplementedError:
    -            raise RuntimeError('You may have provided an incorrect file '
    -                               'version. The mat file was probably saved as '
    -                               'version 7.3 (hdf5).')
    +            raise RuntimeError(
    +                "You may have provided an incorrect file "
    +                "version. The mat file was probably saved as "
    +                "version 7.3 (hdf5)."
    +            )
             mat_data = m[array_name]  # loadmat returns a dict containing variables
         else:
    -        raise ValueError('Unkown file version: {0}.'.format(file_version))
    +        raise ValueError("Unkown file version: {0}.".format(file_version))
     
         # Create output: IDTxl data object, list of labels, sampling info in unit
         # time steps (sampling rate of 1).
    -    print('Creating Data object from matlab array: {0}.'.format(array_name))
    +    print("Creating Data object from matlab array: {0}.".format(array_name))
         data = Data(mat_data, dim_order=dim_order, normalise=normalise)
         return data
    @@ -459,39 +472,45 @@

    Source code for idtxl.idtxl_io

         # Replace time index of current value to be consistent with lag-notation
         # in exported graph. Remember the current value's index to later define the
         # proper candidate set.
    -    current_value = (results.get_single_target(
    -        target=target, fdr=fdr)['current_value'][0], 0)
    -    idx_current_value = results.get_single_target(
    -        target=target, fdr=fdr)['current_value'][1]
    +    current_value = (
    +        results.get_single_target(target=target, fdr=fdr)["current_value"][0],
    +        0,
    +    )
    +    idx_current_value = results.get_single_target(target=target, fdr=fdr)[
    +        "current_value"
    +    ][1]
         # Add the target as a node and add omnibus p-value as an attribute
         # of the target node
    -    graph.add_node(current_value,
    -                   omnibus_te=results.get_single_target(
    -                                target=target, fdr=fdr)['omnibus_te'],
    -                   omnibus_sign=results.get_single_target(
    -                                target=target, fdr=fdr)['omnibus_te'])
    +    graph.add_node(
    +        current_value,
    +        omnibus_te=results.get_single_target(target=target, fdr=fdr)["omnibus_te"],
    +        omnibus_sign=results.get_single_target(target=target, fdr=fdr)["omnibus_te"],
    +    )
         # Get selected source variables
    -    selected_vars_sources = results.get_single_target(
    -        target=target, fdr=fdr)['selected_vars_sources']
    +    selected_vars_sources = results.get_single_target(target=target, fdr=fdr)[
    +        "selected_vars_sources"
    +    ]
         # Get selected target variables
    -    selected_vars_target = results.get_single_target(
    -        target=target, fdr=fdr)['selected_vars_target']
    +    selected_vars_target = results.get_single_target(target=target, fdr=fdr)[
    +        "selected_vars_target"
    +    ]
     
         if sign_sources:  # Add only significant past variables as nodes.
             graph.add_nodes_from(selected_vars_sources)
             graph.add_nodes_from(selected_vars_target)
    -    else:   # Add all tested past variables as nodes.
    +    else:  # Add all tested past variables as nodes.
             # Get all sample indices using the current value's actual index.
             samples_tested = np.arange(
                 idx_current_value - results.settings.min_lag_sources,
                 idx_current_value - results.settings.max_lag_sources,
    -            -results.settings.tau_sources)
    +            -results.settings.tau_sources,
    +        )
             # Get source indices
    -        sources_tested = results.get_single_target(
    -            target=target, fdr=fdr)['sources_tested']
    +        sources_tested = results.get_single_target(target=target, fdr=fdr)[
    +            "sources_tested"
    +        ]
             # Create tuples from source and sample indices
    -        tested_vars_sources = [i for i in it.product(
    -            sources_tested, samples_tested)]
    +        tested_vars_sources = [i for i in it.product(sources_tested, samples_tested)]
             graph.add_nodes_from(tested_vars_sources)
     
         # Add edges from selected target variables to the target.
    @@ -499,16 +518,21 @@ 

    Source code for idtxl.idtxl_io

             graph.add_edge(v, current_value)
     
         # Get TE and p-values fro selected source variables
    -    selected_sources_te = results.get_single_target(
    -        target=target, fdr=fdr)['selected_sources_te']
    -    selected_sources_pval = results.get_single_target(
    -        target=target, fdr=fdr)['selected_sources_pval']
    +    selected_sources_te = results.get_single_target(target=target, fdr=fdr)[
    +        "selected_sources_te"
    +    ]
    +    selected_sources_pval = results.get_single_target(target=target, fdr=fdr)[
    +        "selected_sources_pval"
    +    ]
         # Add edges from selected source variables to the target.
         # Also add TE and p-value as edge attributes
    -    for (ind, v) in enumerate(selected_vars_sources):
    -        graph.add_edge(v, current_value,
    -                       te=selected_sources_te[ind],
    -                       pval=selected_sources_pval[ind])
    +    for ind, v in enumerate(selected_vars_sources):
    +        graph.add_edge(
    +            v,
    +            current_value,
    +            te=selected_sources_te[ind],
    +            pval=selected_sources_pval[ind],
    +        )
         return graph
    @@ -553,40 +577,41 @@

    Source code for idtxl.idtxl_io

         # node labels is a list of '-' (no labels).
         n_nodes = adjacency_matrix.n_nodes()
         n_edges = adjacency_matrix.n_edges()
    -    labels = kwargs.get('labels', ['-' for i in range(n_nodes)])
    -    node_color = kwargs.get('node_color', np.ones(n_nodes))
    -    node_size = kwargs.get('node_size', np.ones(n_nodes))
    +    labels = kwargs.get("labels", ["-" for i in range(n_nodes)])
    +    node_color = kwargs.get("node_color", np.ones(n_nodes))
    +    node_size = kwargs.get("node_size", np.ones(n_nodes))
         if n_edges == 0:
    -        Warning('No edges in results file. Nothing to plot.')
    -    assert mni_coord.shape[0] == n_nodes and mni_coord.shape[1] == 3, (
    -        'MNI coordinates must have shape [n_nodes, 3].')
    -    assert len(labels) == n_nodes, (
    -        'Labels must have same length as no. nodes.')
    -    assert len(node_color) == n_nodes, (
    -        'Node colors must have same length as no. nodes.')
    -    assert len(node_size) == n_nodes, (
    -        'Node size must have same length as no. nodes.')
    +        Warning("No edges in results file. Nothing to plot.")
    +    assert (
    +        mni_coord.shape[0] == n_nodes and mni_coord.shape[1] == 3
    +    ), "MNI coordinates must have shape [n_nodes, 3]."
    +    assert len(labels) == n_nodes, "Labels must have same length as no. nodes."
    +    assert len(node_color) == n_nodes, "Node colors must have same length as no. nodes."
    +    assert len(node_size) == n_nodes, "Node size must have same length as no. nodes."
     
         # Check, if there are blanks in the labels and delete them, otherwise
         # BrainNet viewer chrashes
         labels_stripped = [l.replace(" ", "") for l in labels]
     
         # Write node file.
    -    with open('{0}.node'.format(file_name), 'w') as text_file:
    +    with open("{0}.node".format(file_name), "w") as text_file:
             for n in range(n_nodes):
    -            print('{0}\t{1}\t{2}\t'.format(*mni_coord[n, :]),
    -                  file=text_file, end='')
    -            print('{0}\t{1}\t'.format(node_color[n], node_size[n]),
    -                  file=text_file, end='')
    -            print('{0}'.format(labels_stripped[n]), file=text_file)
    +            print("{0}\t{1}\t{2}\t".format(*mni_coord[n, :]), file=text_file, end="")
    +            print(
    +                "{0}\t{1}\t".format(node_color[n], node_size[n]), file=text_file, end=""
    +            )
    +            print("{0}".format(labels_stripped[n]), file=text_file)
     
         # Write edge file.
    -    with open('{0}.edge'.format(file_name), 'w') as text_file:
    +    with open("{0}.edge".format(file_name), "w") as text_file:
             for i in range(n_nodes):
                 for j in range(n_nodes):
    -                print('{0}\t'.format(adjacency_matrix.edge_matrix[i, j]),
    -                      file=text_file, end='')
    -            print('', file=text_file)
    + print( + "{0}\t".format(adjacency_matrix.edge_matrix[i, j]), + file=text_file, + end="", + ) + print("", file=text_file)
    @@ -618,7 +643,7 @@

    Navigation

  • modules |
  • - + diff --git a/docs/html/_modules/idtxl/idtxl_utils.html b/docs/html/_modules/idtxl/idtxl_utils.html index 0da3b5e1..8e8fc0c2 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 documentation + idtxl.idtxl_utils — IDTxl 1.5.1 documentation @@ -31,7 +31,7 @@

    Navigation

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

    Source code for idtxl.idtxl_utils

         """
         if i_1 > i_2:
             i_1, i_2 = i_2, i_1
    -    return ''.join([s[0:i_1], s[i_2], s[i_1+1:i_2], s[i_1], s[i_2+1:]])
    + return "".join([s[0:i_1], s[i_2], s[i_1 + 1 : i_2], s[i_1], s[i_2 + 1 :]])