From 70206a70e6652507771a29074c8f066b3dc94249 Mon Sep 17 00:00:00 2001 From: EmilianoMarchese Date: Fri, 26 Feb 2021 16:57:38 +0100 Subject: [PATCH 1/4] v2.0.2: Updated "download_url" field with the link to the last release. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 2d8587b0..1257e2e7 100644 --- a/setup.py +++ b/setup.py @@ -23,7 +23,7 @@ long_description_content_type="text/markdown", license="GNU General Public License v3", url="https://github.com/nicoloval/NEMtropy/", - download_url="https://github.com/nicoloval/NEMtropy/archive/v2.0.0.zip", + download_url="https://github.com/nicoloval/NEMtropy/archive/v2.0.2.zip", keywords=['Network reconstruction', 'Networks Null Models', 'Maximum Entrophy Methods'], classifiers=[ From 3974832682d64c9205fb7583738b677d743dc44c Mon Sep 17 00:00:00 2001 From: mat701 Date: Tue, 9 Mar 2021 12:45:58 +0100 Subject: [PATCH 2/4] Fixed bug in bicm, refined documentation --- docs/source/index.rst | 20 +++-- src/NEMtropy/graph_classes.py | 137 +++++++++++++++++++++--------- src/NEMtropy/network_functions.py | 127 ++++++++++++++++++--------- tests/bicm_test.py | 4 +- 4 files changed, 197 insertions(+), 91 deletions(-) diff --git a/docs/source/index.rst b/docs/source/index.rst index 75205e17..8e0c5918 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -42,18 +42,21 @@ to preserve defines the model you need: - **DBCM** *Directed Binary Configuration Model* `[1] <#1>`__ - **DECM** *Directed Enhanced Configuration Model* `[1] <#1>`__ - **CReMa** `[2] <#2>`__ +- **BiCM** *Bipartite Configuration Model* `[3] <#3>`__ The following table may helps you identify the model that fits your needs in function of the type of network you are working with; for in-depth discussion please see the references. -+----------------------+--------------------+-------------------+ -| [...] | Undirected Graph | Directed Graph | -+======================+====================+===================+ -| **Binary Graph** | *UBCM* | *DBCM* | -+----------------------+--------------------+-------------------+ -| **Weighted Graph** | *UECM*, *CReMa* | *DECM*, *CReMa* | -+----------------------+--------------------+-------------------+ ++----------------------+--------------------+-------------------+-------------------+ +| [...] | Undirected Graph | Directed Graph | Bipartite Graph | ++======================+====================+===================+===================+ +| **Binary Graph** | *UBCM* | *DBCM* | *BiCM* | ++----------------------+--------------------+-------------------+-------------------+ +| **Weighted Graph** | *UECM*, *CReMa* | *DECM*, *CReMa* | - | ++----------------------+--------------------+-------------------+-------------------+ + +The BiCM module is also available as `a standalone package `__, find its docs `here `__. *References* @@ -64,6 +67,9 @@ in-depth discussion please see the references. faster horse on a safer trail: generalized inference for the efficient reconstruction of weighted networks." New Journal of Physics 22.5 (2020): 053053. https://arxiv.org/abs/1811.09829 +- [3] Saracco, Fabio, Riccardo Di Clemente, Andrea Gabrielli, and Tiziano Squartini. + "Randomizing bipartite networks: the case of the World Trade Web." + Scientific reports 5, no. 1 (2015): 1-18. https://doi.org/10.1038/srep10595 Installation diff --git a/src/NEMtropy/graph_classes.py b/src/NEMtropy/graph_classes.py index 77a642ea..768d7957 100644 --- a/src/NEMtropy/graph_classes.py +++ b/src/NEMtropy/graph_classes.py @@ -778,7 +778,7 @@ def _initialize_problem(self, model, method): if self.regularise == "eigenvalues": self.hessian_regulariser = sof.matrix_regulariser_function_eigen_based elif self.regularise == "identity": - self.hessian_regulariser = hessian_regulariser_function + self.hessian_regulariser = sof.matrix_regulariser_function def _solve_problem_crema_undirected( self, @@ -2654,7 +2654,6 @@ def __init__(self, biadjacency=None, adjacency_list=None, edgelist=None, degree_ self.v_adj_list = None self.projection_method = 'poisson' self.threads_num = 1 - self.progress_bar = True self.rows_pvals = None self.cols_pvals = None self.is_rows_projected = False @@ -2806,9 +2805,8 @@ def _set_initial_guess(self): """ Internal method to set the initial point of the solver. """ - if self.initial_guess is None: - self.r_x = self.r_rows_deg / ( - np.sqrt(self.r_n_edges)) # This +1 increases the stability of the solutions. + if self.initial_guess is None: # Chung-Lu approximation + self.r_x = self.r_rows_deg / (np.sqrt(self.r_n_edges)) self.r_y = self.r_cols_deg / (np.sqrt(self.r_n_edges)) elif self.initial_guess == 'random': self.r_x = np.random.rand(self.r_n_rows).astype(np.float64) @@ -3027,7 +3025,8 @@ def _solve_root(self): @staticmethod def check_sol(biad_mat, avg_bicm, return_error=False, in_place=False): """ - This function prints the rows sums differences between two matrices, that originally are the biadjacency matrix and its bicm2 average matrix. + Static method. + This function prints the rows sums differences between two matrices, that originally are the biadjacency matrix and its bicm average matrix. The intended use of this is to check if an average matrix is actually a solution for a bipartite configuration model. If return_error is set to True, it returns 1 if the sum of the differences is bigger than 1. @@ -3069,15 +3068,14 @@ def check_sol(biad_mat, avg_bicm, return_error=False, in_place=False): else: return - @staticmethod - def check_sol_light(x, y, rows_deg, cols_deg, return_error=False): + def check_sol_light(self, return_error=False): """ Light version of the check_sol function, working only on the fitnesses and the degree sequences. """ error = 0 rows_error_vec = [] - for i in range(len(x)): - row_avgs = x[i] * y / (1 + x[i] * y) + for i in range(self.r_n_rows): + row_avgs = self.r_x[i] * self.r_y / (1 + self.r_x[i] * self.r_y) if error == 0: if np.any(row_avgs < 0): print('Warning: negative link probabilities') @@ -3085,11 +3083,12 @@ def check_sol_light(x, y, rows_deg, cols_deg, return_error=False): if np.any(row_avgs > 1): print('Warning: link probabilities > 1') error = 1 - rows_error_vec.append(np.sum(row_avgs) - rows_deg[i]) + rows_error_vec.append(np.sum(self.cols_multiplicity * row_avgs) - self.r_rows_deg[i]) rows_error_vec = np.abs(rows_error_vec) err_rows = np.max(rows_error_vec) print('max rows error =', err_rows) - cols_error_vec = np.abs([(x * y[j] / (1 + x * y[j])).sum() - cols_deg[j] for j in range(len(y))]) + cols_error_vec = np.abs([(self.rows_multiplicity * self.r_x * self.r_y[j] / (1 + self.r_x * self.r_y[j])).sum() + - self.r_cols_deg[j] for j in range(self.r_n_cols)]) err_cols = np.max(cols_error_vec) print('max columns error =', err_cols) tot_err = np.sum(rows_error_vec) + np.sum(cols_error_vec) @@ -3120,10 +3119,7 @@ def _check_solution(self, return_error=False, in_place=False): if self.biadjacency is not None and self.avg_mat is not None: return self.check_sol(self.biadjacency, self.avg_mat, return_error=return_error, in_place=in_place) else: - return self.check_sol_light(self.x[self.nonfixed_rows], self.y[self.nonfixed_cols], - self.rows_deg[self.nonfixed_rows] - self.full_cols_num, - self.cols_deg[self.nonfixed_cols] - self.full_rows_num, - return_error=return_error) + return self.check_sol_light(return_error=return_error) def _set_solved_problem(self, solution): """ @@ -3283,10 +3279,6 @@ def solve_tool( :param bool print_error: Print the final error of the solution :param bool exp: if this is set to true the solver works with the reparameterization $x_i = e^{-\theta_i}$, $y_\alpha = e^{-\theta_\alpha}$. It might be slightly faster but also might not converge. - :param full_return: If True the algorithm returns more statistics than the obtained solution, defaults to False. - :type full_return: bool, optional - :param eps: parameter controlling the tolerance of the difference between two iterations, defaults to 1e-8. - :type eps: float, optional """ if not self.is_initialized: print('Graph is not initialized. I can\'t compute the BiCM.') @@ -3321,6 +3313,41 @@ def solve_tool( print('Solver did not converge.') self.is_randomized = True + def solve_bicm( + self, + method='newton', + initial_guess=None, + light_mode=None, + tolerance=None, + tol=1e-8, + eps=1e-8, + max_steps=None, + verbose=False, + linsearch=True, + regularise=None, + print_error=True, + full_return=False, + exp=False): + """ + Deprecated method, replaced by solve_tool + """ + if tolerance is not None: + tol = tolerance + print('solve_bicm has been deprecated, calling solve_tool instead') + self.solve_tool( + method=method, + initial_guess=initial_guess, + light_mode=light_mode, + tol=tol, + eps=eps, + max_steps=max_steps, + verbose=verbose, + linsearch=linsearch, + regularise=regularise, + print_error=print_error, + full_return=full_return, + exp=exp) + def get_bicm_matrix(self): """Get the matrix of probabilities of the BiCM. If the BiCM has not been computed, it also computes it with standard settings. @@ -3351,7 +3378,11 @@ def get_bicm_fitnesses(self): self.solve_tool() return self.dict_x, self.dict_y - def _pval_calculator(self, v_list_key, x, y): + def get_fitnesses(self): + """See get_bicm_fitnesses.""" + self.get_bicm_fitnesses() + + def pval_calculator(self, v_list_key, x, y): """ Calculate the p-values of the v-motifs numbers of one vertices and all its neighbours. @@ -3382,12 +3413,13 @@ def _pval_calculator(self, v_list_key, x, y): temp_pvals_dict[neighbor] = max(min(pval, 1), 0) return temp_pvals_dict - def _pval_calculator_poibin(self, deg_couple, deg_dict, x, y): + def pval_calculator_poibin(self, deg_couple, deg_dict, degs, x, y): """ Calculate the p-values of the v-motifs numbers of all nodes with a given couple degrees. :param tuple deg_couple: the couple of degrees considered. - :param dict deg_dict: the dictionary containing for each degree the list of nodes of that degree. + :param tuple deg_couple: the couple of degrees considered. + :param tuple deg_couple: the couple of degrees considered. :param numpy.ndarray x: the fitnesses of the layer of the desired projection. :param numpy.ndarray y: the fitnesses of the opposite layer. :returns: a list containing 3-tuples with the two nodes considered and their p-value. @@ -3432,7 +3464,7 @@ def _projection_calculator(self): pval_adj_list = dict() if self.threads_num > 1: with Pool(processes=self.threads_num) as pool: - partial_function = partial(self._pval_calculator, x=x, y=y) + partial_function = partial(self.pval_calculator, x=x, y=y) if self.progress_bar: pvals_dicts = pool.map(partial_function, tqdm(v_list_keys)) else: @@ -3443,10 +3475,10 @@ def _projection_calculator(self): else: if self.progress_bar: for k in tqdm(v_list_keys): - pval_adj_list[k] = self._pval_calculator(k, x=x, y=y) + pval_adj_list[k] = self.pval_calculator(k, x=x, y=y) else: for k in v_list_keys: - pval_adj_list[k] = self._pval_calculator(k, x=x, y=y) + pval_adj_list[k] = self.pval_calculator(k, x=x, y=y) else: if self.rows_projection: degs = self.rows_deg @@ -3465,7 +3497,7 @@ def _projection_calculator(self): print('Calculating p-values...') if self.threads_num > 1: with Pool(processes=self.threads_num) as pool: - partial_function = partial(self._pval_calculator_poibin, deg_dict=deg_dict, x=x, y=y) + partial_function = partial(self.pval_calculator_poibin, deg_dict=deg_dict, degs=degs, x=x, y=y) if self.progress_bar: pvals_dicts = pool.map(partial_function, tqdm(deg_couples)) else: @@ -3475,11 +3507,11 @@ def _projection_calculator(self): if self.progress_bar: for deg_couple in tqdm(deg_couples): pvals_dicts.append( - self._pval_calculator_poibin(deg_couple, deg_dict=deg_dict, x=x, y=y)) + self.pval_calculator_poibin(deg_couple, deg_dict=deg_dict, degs=degs, x=x, y=y)) else: for deg_couple in v_list_coupled: pvals_dicts.append( - self._pval_calculator_poibin(deg_couple, deg_dict=deg_dict, x=x, y=y)) + self.pval_calculator_poibin(deg_couple, deg_dict=deg_dict, degs=degs, x=x, y=y)) pval_adj_list = {k: dict() for k in self.v_adj_list} for pvals_dict in pvals_dicts: for node in pvals_dict: @@ -3539,7 +3571,7 @@ def _pvals_validator(self, pval_list, alpha=0.05): multiplier = 2 * alpha / (self.n_cols * (self.n_cols - 1)) try: eff_fdr_pos = np.where(sorted_pvals <= (np.arange(1, len(sorted_pvals) + 1) * alpha * multiplier))[0][-1] - except IndexError: + except: print('No V-motifs will be validated. Try increasing alpha') eff_fdr_pos = 0 eff_fdr_th = eff_fdr_pos * multiplier @@ -3567,12 +3599,13 @@ def _projection_from_pvals(self, alpha=0.05): projected_adj_list[node] = [] projected_adj_list[node].append(neighbor) return projected_adj_list - # return np.array([(v[0], v[1]) for v in self.rows_pvals if v[2] <= eff_fdr_th]) - # else: - # eff_fdr_th = nef.pvals_validator([v[2] for v in self.cols_pvals], self.n_cols, alpha=alpha) - # return np.array([(v[0], v[1]) for v in self.cols_pvals if v[2] <= eff_fdr_th]) - def get_rows_projection(self, alpha=0.05, method='poisson', threads_num=None, progress_bar=True): + def get_rows_projection(self, + alpha=0.05, + method='poisson', + threads_num=None, + progress_bar=True, + fmt='adjacency_list'): """Get the projected network on the rows layer of the graph. :param alpha: threshold for the validation of the projected edges. @@ -3584,23 +3617,36 @@ def get_rows_projection(self, alpha=0.05, method='poisson', threads_num=None, pr the computation is not parallelized. :type threads_num: int, optional :param bool progress_bar: Show the progress bar - :returns: edgelist of the projected network on the rows layer - :rtype: numpy.array + :param str fmt: the desired format for the output + :returns: the projected network on the rows layer, in the format specified by fmt """ if not self.is_rows_projected: self.compute_projection(rows=True, alpha=alpha, method=method, threads_num=threads_num, progress_bar=progress_bar) + + if fmt == 'matrix': + return nef.adjacency_matrix_from_adjacency_list(self.projected_rows_adj_list, fmt='array') + elif fmt == 'sparse': + return nef.adjacency_matrix_from_adjacency_list(self.projected_rows_adj_list, fmt='sparse') if self.rows_dict is None: - return self.projected_rows_adj_list + adj_list_to_return = self.projected_rows_adj_list else: adj_list_to_return = {} for node in self.projected_rows_adj_list: adj_list_to_return[self.rows_dict[node]] = [] for neighbor in self.projected_rows_adj_list[node]: adj_list_to_return[self.rows_dict[node]].append(self.rows_dict[neighbor]) + if fmt == 'adjacency_list': return adj_list_to_return - - def get_cols_projection(self, alpha=0.05, method='poisson', threads_num=4, progress_bar=True): + elif fmt == 'edgelist': + nef.edgelist_from_adjacency_list_bipartite(adj_list_to_return) + + def get_cols_projection(self, + alpha=0.05, + method='poisson', + threads_num=None, + progress_bar=True, + fmt='adjacency_list'): """Get the projected network on the columns layer of the graph. :param alpha: threshold for the validation of the projected edges. @@ -3612,12 +3658,16 @@ def get_cols_projection(self, alpha=0.05, method='poisson', threads_num=4, progr the computation is not parallelized. :type threads_num: int, optional :param bool progress_bar: Show the progress bar - :returns: edgelist of the projected network on the columns layer - :rtype: numpy.array + :param str fmt: the desired format for the output + :returns: the projected network on the columns layer, in the format specified by fmt """ if not self.is_cols_projected: self.compute_projection(rows=False, alpha=alpha, method=method, threads_num=threads_num, progress_bar=progress_bar) + if fmt == 'biadjacency': + return nef.biadjacency_from_adjacency_list(self.projected_rows_adj_list, fmt='array') + elif fmt == 'sparse': + return nef.biadjacency_from_adjacency_list(self.projected_rows_adj_list, fmt='sparse') if self.cols_dict is None: return self.projected_cols_adj_list else: @@ -3626,7 +3676,10 @@ def get_cols_projection(self, alpha=0.05, method='poisson', threads_num=4, progr adj_list_to_return[self.cols_dict[node]] = [] for neighbor in self.projected_cols_adj_list[node]: adj_list_to_return[self.cols_dict[node]].append(self.cols_dict[neighbor]) + if fmt == 'adjacency_list': return adj_list_to_return + elif fmt == 'edgelist': + nef.edgelist_from_adjacency_list_bipartite(adj_list_to_return) def set_biadjacency_matrix(self, biadjacency): """Set the biadjacency matrix of the graph. diff --git a/src/NEMtropy/network_functions.py b/src/NEMtropy/network_functions.py index 3026f027..9bdca96e 100644 --- a/src/NEMtropy/network_functions.py +++ b/src/NEMtropy/network_functions.py @@ -437,7 +437,7 @@ def edgelist_from_edgelist_directed(edgelist): @jit(nopython=True) def bicm_from_fitnesses(x, y): """ - Rebuilds the average probability matrix of the bicm2 from the fitnesses + Rebuilds the average probability matrix of the bicm from the fitnesses :param x: the fitness vector of the rows layer :type x: numpy.ndarray @@ -459,9 +459,7 @@ def sample_bicm(avg_mat): if not isinstance(avg_mat, np.ndarray): avg_mat = np.array(avg_mat) dim1, dim2 = avg_mat.shape - return np.array( - avg_mat > np.reshape(np.random.sample(dim1 * dim2), (dim1, dim2)), - dtype=int) + return np.array(avg_mat > np.reshape(np.random.sample(dim1 * dim2), (dim1, dim2)), dtype=int) def sample_bicm_edgelist(x, y): @@ -512,15 +510,13 @@ def edgelist_from_biadjacency(biadjacency): coords = biadjacency.nonzero() if np.sum(biadjacency.data != 1) > 0: raise ValueError('Only binary matrices') - return np.array(list(zip(coords[0], coords[1])), - dtype=np.dtype([('rows', int), ('columns', int)])), \ - np.array(biadjacency.sum(1)).flatten(), np.array( - biadjacency.sum(0)).flatten() + return np.array(list(zip(coords[0], coords[1])), dtype=np.dtype([('rows', int), ('columns', int)])),\ + np.array(biadjacency.sum(1)).flatten(), np.array(biadjacency.sum(0)).flatten() else: if np.sum(biadjacency[biadjacency != 0] != 1) > 0: raise ValueError('Only binary matrices') return np.array(edgelist_from_biadjacency_fast(biadjacency), - dtype=np.dtype([('rows', int), ('columns', int)])), \ + dtype=np.dtype([('rows', int), ('columns', int)])),\ np.sum(biadjacency, axis=1), np.sum(biadjacency, axis=0) @@ -529,15 +525,13 @@ def biadjacency_from_edgelist(edgelist, fmt='array'): Build the biadjacency matrix of a bipartite network from its edgelist. Returns a matrix of the type specified by ``fmt``, by default a numpy array. """ - edgelist, rows_deg, cols_deg, rows_dict, cols_dict = edgelist_from_edgelist_bipartite( - edgelist) + edgelist, rows_deg, cols_deg, rows_dict, cols_dict = edgelist_from_edgelist_bipartite(edgelist) if fmt == 'array': biadjacency = np.zeros((len(rows_deg), len(cols_deg)), dtype=int) for edge in edgelist: biadjacency[edge[0], edge[1]] = 1 elif fmt == 'sparse': - biadjacency = scipy.sparse.coo_matrix( - (np.ones(len(edgelist)), (edgelist['rows'], edgelist['columns']))) + biadjacency = scipy.sparse.coo_matrix((np.ones(len(edgelist)), (edgelist['rows'], edgelist['columns']))) elif not isinstance(format, str): raise TypeError('format must be a string (either "array" or "sparse")') else: @@ -552,8 +546,7 @@ def edgelist_from_edgelist_bipartite(edgelist): Returns also two dictionaries that keep track of the nodes. """ edgelist = np.array(list(set([tuple(edge) for edge in edgelist]))) - out = np.zeros(np.shape(edgelist)[0], - dtype=np.dtype([('source', object), ('target', object)])) + out = np.zeros(np.shape(edgelist)[0], dtype=np.dtype([('source', object), ('target', object)])) out['source'] = edgelist[:, 0] out['target'] = edgelist[:, 1] edgelist = out @@ -563,10 +556,8 @@ def edgelist_from_edgelist_bipartite(edgelist): cols_dict = dict(enumerate(unique_cols)) inv_rows_dict = {v: k for k, v in rows_dict.items()} inv_cols_dict = {v: k for k, v in cols_dict.items()} - edgelist_new = [(inv_rows_dict[edge[0]], inv_cols_dict[edge[1]]) for edge - in edgelist] - edgelist_new = np.array(edgelist_new, - dtype=np.dtype([('rows', int), ('columns', int)])) + edgelist_new = [(inv_rows_dict[edge[0]], inv_cols_dict[edge[1]]) for edge in edgelist] + edgelist_new = np.array(edgelist_new, dtype=np.dtype([('rows', int), ('columns', int)])) return edgelist_new, rows_degs, cols_degs, rows_dict, cols_dict @@ -579,8 +570,7 @@ def adjacency_list_from_edgelist_bipartite(edgelist, convert_type=True): Returns also two dictionaries that keep track of the nodes and the two degree sequences. """ if convert_type: - edgelist, rows_degs, cols_degs, rows_dict, cols_dict = edgelist_from_edgelist_bipartite( - edgelist) + edgelist, rows_degs, cols_degs, rows_dict, cols_dict = edgelist_from_edgelist_bipartite(edgelist) adj_list = {} inv_adj_list = {} for edge in edgelist: @@ -596,36 +586,34 @@ def adjacency_list_from_edgelist_bipartite(edgelist, convert_type=True): def adjacency_list_from_adjacency_list_bipartite(old_adj_list): """ - Creates the adjacency list from another adjacency list, convering the data type. + Creates the adjacency list from another adjacency list, converting the data type to integers. Method for bipartite networks. - Returns two dictionaries, each containing an adjacency list with the rows as keys and the columns as keys, respectively. + Returns two dictionaries, each representing an adjacency list with the rows or columns as keys, respectively. Original keys are treated as rows, values as columns. - The nodes are enumerated and the adjacency list is returned as integers. + The nodes are enumerated and the adjacency list contains the related integers. Returns also two dictionaries that keep track of the nodes and the two degree sequences. """ rows_dict = dict(enumerate(np.unique(list(old_adj_list.keys())))) - cols_dict = dict(enumerate( - np.unique([el for mylist in old_adj_list.values() for el in mylist]))) + cols_dict = dict(enumerate(np.unique([el for l in old_adj_list.values() for el in l]))) inv_rows_dict = {v: k for k, v in rows_dict.items()} inv_cols_dict = {v: k for k, v in cols_dict.items()} adj_list = {} inv_adj_list = {} for k in old_adj_list: - adj_list.setdefault(inv_rows_dict[k], set()).update( - {inv_cols_dict[val] for val in old_adj_list[k]}) + adj_list.setdefault(inv_rows_dict[k], set()).update({inv_cols_dict[val] for val in old_adj_list[k]}) for val in old_adj_list[k]: - inv_adj_list.setdefault(inv_cols_dict[val], set()).add( - inv_rows_dict[k]) - rows_degs = np.array([len(adj_list[k]) for k in adj_list]) - cols_degs = np.array([len(inv_adj_list[k]) for k in inv_adj_list]) + inv_adj_list.setdefault(inv_cols_dict[val], set()).add(inv_rows_dict[k]) + rows_degs = np.array([len(adj_list[k]) for k in range(len(adj_list))]) + cols_degs = np.array([len(inv_adj_list[k]) for k in range(len(inv_adj_list))]) return adj_list, inv_adj_list, rows_degs, cols_degs, rows_dict, cols_dict -def adjacency_list_from_biadjacency(biadjacency): +def adjacency_list_from_biadjacency(biadjacency, return_inverse=True, return_degree_sequences=True): """ Creates the adjacency list from a biadjacency matrix, given in sparse format or as a list or numpy array. - Returns two dictionaries, each containing an adjacency list with the rows as keys and the columns as keys, respectively. - Returns also the two degree sequences. + Returns the adjacency list as a dictionary, with the rows as keys and as values lists with indexes of the columns. + If return_inverse is True, the inverse adjacency list is also returned. + If return_degree_sequences is True, the two degree sequences are also returned. """ if scipy.sparse.isspmatrix(biadjacency): if np.sum(biadjacency.data != 1) > 0: @@ -640,8 +628,67 @@ def adjacency_list_from_biadjacency(biadjacency): inv_adj_list = {} for edge_i in range(len(coords[0])): adj_list.setdefault(coords[0][edge_i], set()).add(coords[1][edge_i]) - inv_adj_list.setdefault(coords[1][edge_i], set()).add( - coords[0][edge_i]) - rows_degs = np.array([len(adj_list[k]) for k in adj_list]) - cols_degs = np.array([len(inv_adj_list[k]) for k in inv_adj_list]) - return adj_list, inv_adj_list, rows_degs, cols_degs + inv_adj_list.setdefault(coords[1][edge_i], set()).add(coords[0][edge_i]) + return_args = [adj_list] + if return_inverse: + return_args.append(inv_adj_list) + if return_degree_sequences: + rows_degs = np.array([len(adj_list[k]) for k in range(len(adj_list))]) + cols_degs = np.array([len(inv_adj_list[k]) for k in range(len(inv_adj_list))]) + return_args.append(rows_degs) + return_args.append(cols_degs) + if len(return_args) > 1: + return tuple(return_args) + else: + return adj_list + + +def edgelist_from_adjacency_list_bipartite(adj_list): + """ + Creates the edgelist from an adjacency list given as a dictionary. + Returns the edgelist as a numpy array, with the keys as first elements of the couples and the values second. + :param dict adj_list: the adjacency list to be converted. + """ + edgelist = [] + for k in adj_list: + for k_neighbor in adj_list[k]: + edgelist.append((k, k_neighbor)) + return np.array(edgelist) + + +def biadjacency_from_adjacency_list(adj_list, fmt='array'): + """ + Creates the biadjacency matrix from an adjacency list given as a dictionary. + Returns the biadjacency as a numpy array by default, or sparse scipy matrix if fmt='sparse'. + The biadjacency comes with the keys as rows of the matrix and the values as columns. + :param dict adj_list: the adjacency list to be converted. Must contain integers that will be used as indexes. + :param str fmt: the desired format of the output biadjacency matrix, either 'array' or 'sparse', optional + """ + assert np.isin(fmt, ['array', 'sparse']), 'fmt must be either array or sparse' + assert isinstance(list(adj_list.keys())[0], (int, float, complex)), 'Adjacency list must be numeric' + rows_index = [k for k, v in adj_list.items() for _ in range(len(v))] + cols_index = [i for ids in adj_list.values() for i in ids] + biad_mat = scipy.sparse.csr_matrix(([1] * len(rows_index), (rows_index, cols_index))) + if fmt == 'sparse': + return biad_mat + else: + return biad_mat.toarray() + + +def adjacency_matrix_from_adjacency_list(adj_list, fmt='array'): + """ + Creates the adjacency matrix from an adjacency list given as a dictionary. + Returns the adjacency as a numpy array by default, or sparse scipy matrix if fmt='sparse'. + :param dict adj_list: the adjacency list to be converted. Must contain integers that will be used as indexes. + :param str fmt: the desired format of the output adjacency matrix, either 'array' or 'sparse', optional + """ + assert np.isin(fmt, ['array', 'sparse']), 'fmt must be either array or sparse' + assert isinstance(list(adj_list.keys())[0], (int, float, complex)), 'Adjacency list must be numeric' + rows_index = [k for k, v in adj_list.items() for _ in range(len(v))] + cols_index = [i for ids in adj_list.values() for i in ids] + biad_mat = scipy.sparse.csr_matrix(([1] * len(rows_index), (rows_index, cols_index))) + biad_mat = biad_mat + biad_mat.T + if fmt == 'sparse': + return biad_mat + else: + return biad_mat.toarray() diff --git a/tests/bicm_test.py b/tests/bicm_test.py index 23a503f0..5fbe5f0f 100644 --- a/tests/bicm_test.py +++ b/tests/bicm_test.py @@ -42,8 +42,8 @@ myGraph.set_edgelist(edgelist_names) -# And now let's simply compute the bicm2! This should run instantly. The solver checks that the solution is correct automatically. -print('And now let\'s simply compute the bicm2! This should run instantly. The solver checks that the solution is correct automatically.') +# And now let's simply compute the BiCM! This should run instantly. The solver checks that the solution is correct automatically. +print('And now let\'s simply compute the BiCM! This should run instantly. The solver checks that the solution is correct automatically.') myGraph.solve_tool() dict_x, dict_y = myGraph.get_bicm_fitnesses() print('Yielded data type is:', type(dict_x)) From 90df362ce43493f77475cf2e2b04d5311b5a8e56 Mon Sep 17 00:00:00 2001 From: mat701 Date: Wed, 10 Mar 2021 11:14:21 +0100 Subject: [PATCH 3/4] Solved issue 36, modified docstrings and documentations, fixed some TODOs --- docs/source/NEMtropy.rst | 6 + docs/source/conf.py | 6 +- setup.py | 4 +- src/NEMtropy/__init__.py | 4 +- src/NEMtropy/graph_classes.py | 87 +++++------ src/NEMtropy/models_functions.py | 256 +++++++++++++++---------------- 6 files changed, 178 insertions(+), 185 deletions(-) diff --git a/docs/source/NEMtropy.rst b/docs/source/NEMtropy.rst index b9963f72..07867d1d 100644 --- a/docs/source/NEMtropy.rst +++ b/docs/source/NEMtropy.rst @@ -36,3 +36,9 @@ Network functions .. automodule:: NEMtropy.network_functions :members: + +Poibin class +----------------- + +.. automodule:: NEMtropy.poibin + :members: diff --git a/docs/source/conf.py b/docs/source/conf.py index 800dcab9..ae863925 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -19,11 +19,11 @@ # -- Project information ----------------------------------------------------- project = 'NEMtropy' -copyright = "2021, Emiliano Marchese and Nicolo' Vallarano" -author = "Emiliano Marchese, Nicolo' Vallarano" +copyright = "2021, Emiliano Marchese, Nicolo' Vallarano and Matteo Bruno" +author = "Emiliano Marchese, Nicolo' Vallarano, Matteo Bruno" # The full version, including alpha/beta/rc tags -release = '2.0.1' +release = '2.0.3' # -- General configuration --------------------------------------------------- diff --git a/setup.py b/setup.py index 1257e2e7..c30e718e 100644 --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ ' matteo.bruno@imtlucca.it', packages=["NEMtropy"], package_dir={'': 'src'}, - version="2.0.2", + version="2.0.3", description="NEMtropy is a Maximum-Entropy toolbox for networks, it" " provides the user with a state of the art solver for a" " range variety of Maximum Entropy Networks models derived" @@ -25,7 +25,7 @@ url="https://github.com/nicoloval/NEMtropy/", download_url="https://github.com/nicoloval/NEMtropy/archive/v2.0.2.zip", keywords=['Network reconstruction', 'Networks Null Models', - 'Maximum Entrophy Methods'], + 'Maximum Entropy Methods'], classifiers=[ 'License :: OSI Approved :: GNU Library or Lesser General' ' Public License (LGPL)', diff --git a/src/NEMtropy/__init__.py b/src/NEMtropy/__init__.py index f3294356..51586eef 100644 --- a/src/NEMtropy/__init__.py +++ b/src/NEMtropy/__init__.py @@ -3,7 +3,7 @@ from .ensemble_generator import * from .models_functions import * -__version__ = "2.0.2" +__version__ = "2.0.3" __author__ = """Nicolo' Vallarano (nicolo.vallarano@imtlucca.it) -Emiliano Marchese (emiliano.marchese@imtulucca.it) +Emiliano Marchese (emiliano.marchese@imtlucca.it) Matteo Bruno (matteo.bruno@imtlucca.it)""" diff --git a/src/NEMtropy/graph_classes.py b/src/NEMtropy/graph_classes.py index 768d7957..ec5ef951 100644 --- a/src/NEMtropy/graph_classes.py +++ b/src/NEMtropy/graph_classes.py @@ -177,7 +177,7 @@ def _initialize_graph( elif len(degree_sequence) > 0: try: int(degree_sequence[0]) - except: + except (ValueError, TypeError): raise TypeError( "The degree sequence must contain numeric values." ) @@ -198,7 +198,7 @@ def _initialize_graph( elif len(strength_sequence): try: int(strength_sequence[0]) - except: + except (ValueError, TypeError): raise TypeError( "The strength sequence must contain numeric" "values." @@ -230,7 +230,7 @@ def _initialize_graph( elif len(strength_sequence): try: int(strength_sequence[0]) - except: + except (ValueError, TypeError): raise TypeError( "The strength sequence must contain numeric values." ) @@ -246,7 +246,7 @@ def _initialize_graph( def set_adjacency_matrix(self, adjacency): """Initialises graph given the adjacency matrix. - :param adjacency: ajdacency matrix. + :param adjacency: adjacency matrix. :type adjacency: numpy.ndarray, list, scipy.sparse_matrix """ if self.is_initialized: @@ -286,7 +286,7 @@ def set_degree_sequences(self, degree_sequence): self._initialize_graph(degree_sequence=degree_sequence) def clean_edges(self): - """Deletes all the initialiased attributes. + """Deletes all the initialised attributes. """ self.adjacency = None self.edgelist = None @@ -295,7 +295,7 @@ def clean_edges(self): def _solve_problem( self, - initial_guess=None, + initial_guess='random', model="cm", method="quasinewton", max_steps=100, @@ -715,9 +715,9 @@ def _initialize_problem(self, model, method): self.fun = d_fun[mod_met] self.fun_jac = d_fun_jac[mod_met] self.step_fun = d_fun_stop[mod_met] - except: + except KeyError: raise ValueError( - 'Method must be "newton","quasi-newton", or "fixed-point".' + 'Method must be "newton","quasinewton", or "fixed-point".' ) d_pmatrix = { @@ -782,7 +782,7 @@ def _initialize_problem(self, model, method): def _solve_problem_crema_undirected( self, - initial_guess=None, + initial_guess='random', model="crema", adjacency="cm", method="quasinewton", @@ -925,7 +925,7 @@ def solve_tool( self, model, method, - initial_guess=None, + initial_guess='random', adjacency="cm_exp", method_adjacency="newton", initial_guess_adjacency="random", @@ -943,12 +943,12 @@ def solve_tool( :param model: Available models are: - - *cm*: solves UBCM respect to the parameters *x* of the mof.loglikelihood function, it works for uweighted undirected graphs [insert ref]. + - *cm*: solves UBCM respect to the parameters *x* of the mof.loglikelihood function, it works for unweighted undirected graphs [insert ref]. - *cm_exp*: differently from the *cm* option, *cm_exp* considers the exponents of *x* as parameters [insert ref]. - *ecm*: solves UECM respect to the parameters *x* and *y* of the mof.loglikelihood function, it is conceived for weighted undirected graphs [insert ref]. - *ecm_exp*: differently from the *ecm* option, *ecm_exp* considers the exponents of *x* and *y* as parameters [insert ref]. - - *crema*: solves CReMa for a weighted undirectd graphs. In order to compute beta parameters, it requires information about the binary structure of the network. These can be provided by the user by using *adjacency* paramenter. - - *crema-sparse*: alternative implementetio of *crema* for large graphs. The *creama-sparse* model doesn't compute the binary probability matrix avoing memory problems for large graphs. + - *crema*: solves CReMa for a weighted undirected graphs. In order to compute beta parameters, it requires information about the binary structure of the network. These can be provided by the user by using *adjacency* paramenter. + - *crema-sparse*: alternative implementation of *crema* for large graphs. The *creama-sparse* model doesn't compute the binary probability matrix avoing memory problems for large graphs. :type model: str :param method: Available methods to solve the given *model* are: @@ -958,23 +958,23 @@ def solve_tool( - *fixed-point*: uses a fixed-point method to find parameters maximising mof.loglikelihood function. :type method: str - :param initial_guess: Starting point solution may affect the results of the optization process. The user can provid an initial guess or choose between the following options: + :param initial_guess: Starting point solution may affect the results of the optimization process. The user can provide an initial guess or choose between the following options: - **Binary Models**: - *random*: random numbers in (0, 1); - *uniform*: uniform initial guess in (0, 1); - - *degrees*: initial guess of each node is proportianal to its degree; + - *degrees*: initial guess of each node is proportional to its degree; - *degrees_minor*: initial guess of each node is inversely proportional to its degree; - *chung_lu*: initial guess given by Chung-Lu formula; - **Weighted Models**: - *random*: random numbers in (0, 1); - *uniform*: uniform initial guess in (0, 1); - - *strengths*: initial guess of each node is proportianal to its stength; + - *strengths*: initial guess of each node is proportional to its strength; - *strengths_minor*: initial guess of each node is inversely proportional to its strength; :type initial_guess: str, optional :param adjacency: Adjacency can be a binary method (defaults is *cm_exp*) or an adjacency matrix. :type adjacency: str or numpy.ndarray, optional - :param method_adjacency: If adjacency is a *model*, it is the *methdod* used to solve it. Defaults to "newton". + :param method_adjacency: If adjacency is a *model*, it is the *method* used to solve it. Defaults to "newton". :type method_adjacency: str, optional :param initial_guess_adjacency: If adjacency is a *model*, it is the chosen initial guess. Defaults to "random". :type initial_guess_adjacency: str, optional @@ -1304,7 +1304,7 @@ def _initialize_graph( elif len(degree_sequence) > 0: try: int(degree_sequence[0]) - except: # TODO: bare exception + except (ValueError, TypeError): raise TypeError( "The degree sequence must contain numeric values." ) @@ -1330,7 +1330,7 @@ def _initialize_graph( elif len(strength_sequence): try: int(strength_sequence[0]) - except: # TODO: bare exception to check + except (ValueError, TypeError): raise TypeError( ("The strength sequence must contain" " numeric values.") @@ -1366,7 +1366,7 @@ def _initialize_graph( elif len(strength_sequence): try: int(strength_sequence[0]) - except: # TODO: bare exception + except (ValueError, TypeError): raise TypeError( "The strength sequence must contain numeric values." ) @@ -1402,8 +1402,8 @@ def set_adjacency_matrix(self, adjacency): def set_edgelist(self, edgelist): """Initializes a graph from the edgelist. - :param adjacency: Edgelist - :type adjacency: numpy.ndarray, list + :param edgelist: Edgelist + :type edgelist: numpy.ndarray, list """ if self.is_initialized: print( @@ -1416,8 +1416,8 @@ def set_edgelist(self, edgelist): def set_degree_sequences(self, degree_sequence): """Initializes graph from the degrees sequence. - :param adjacency: Degrees sequence - :type adjacency: numpy.ndarray + :param degree_sequence: Degrees sequence + :type degree_sequence: numpy.ndarray """ if self.is_initialized: print( @@ -1437,7 +1437,7 @@ def clean_edges(self): def _solve_problem( self, - initial_guess=None, # TODO:aggiungere un default a initial guess + initial_guess='random', model="dcm", method="quasinewton", max_steps=100, @@ -1907,7 +1907,7 @@ def _solution_error(self): ex_s_out = mof.expected_out_strength_crema_directed( sol, self.adjacency_crema ) - ex_s_in = mof.expected_in_stregth_crema_directed( + ex_s_in = mof.expected_in_strength_crema_directed( sol, self.adjacency_crema ) ex_s = np.concatenate([ex_s_out, ex_s_in]) @@ -2169,7 +2169,7 @@ def _initialize_problem(self, model, method): self.fun = d_fun[mod_met] self.fun_jac = d_fun_jac[mod_met] self.step_fun = d_fun_step[mod_met] - except: # TODO: remove bare excpets + except KeyError: raise ValueError( 'Method must be "newton","quasinewton", or "fixed-point".' ) @@ -2259,7 +2259,7 @@ def _initialize_problem(self, model, method): def _solve_problem_crema_directed( self, - initial_guess=None, + initial_guess='random', model="crema", adjacency="dcm", method="quasinewton", @@ -2381,7 +2381,7 @@ def solve_tool( self, model, method, - initial_guess=None, + initial_guess='random', adjacency=None, method_adjacency='newton', initial_guess_adjacency="random", @@ -2398,25 +2398,25 @@ def solve_tool( The graph should be initialized. :param model: Available models are: - - *dcm*: solves DBCM respect to the parameters *x* and "y" of the loglikelihood function, it works for uweighted directed graphs [insert ref]. + - *dcm*: solves DBCM with respect to the parameters *x* and "y" of the loglikelihood function, it works for unweighted directed graphs [insert ref]. - *dcm_exp*: differently from the *dcm* option, *dcm_exp* considers the exponents of *x* and *y* as parameters [insert ref]. - *decm*: solves DECM respect to the parameters *a_out*, *a_in*, *b_out* and *b_in* of the loglikelihood function, it is conceived for weighted directed graphs [insert ref]. - *decm_exp*: differently from the *decm* option, *decm_exp* considers the exponents of *a_out*, *a_in*, *b_out* and *b_in** as parameters [insert ref]. - - *crema*: solves CReMa for a weighted directd graphs. In order to compute beta parameters, it requires information about the binary structure of the network. These can be provided by the user by using *adjacency* paramenter. - - *crema-sparse*: alternative implementetio of *crema* for large graphs. The *creama-sparse* model doesn't compute the binary probability matrix avoing memory problems for large graphs. + - *crema*: solves CReMa for a weighted directed graphs. In order to compute beta parameters, it requires information about the binary structure of the network. These can be provided by the user by using *adjacency* parameter. + - *crema-sparse*: alternative implementation of *crema* for large graphs. The *creama-sparse* model doesn't compute the binary probability matrix avoiding memory problems for large graphs. :type model: str :param method: Available methods to solve the given *model* are: - *newton*: uses Newton-Rhapson method to solve the selected model, it can be memory demanding for *crema* because it requires the computation of the entire Hessian matrix. This method is not available for *creama-sparse*. - *quasinewton*: uses Newton-Rhapson method with Hessian matrix approximated by its principal diagonal to find parameters maximising loglikelihood function. - *fixed-point*: uses a fixed-point method to find parameters maximising loglikelihood function. :type method: str - :param initial_guess: Starting point solution may affect the results of the optization process. The user can provid an initial guess or choose between the following options: + :param initial_guess: Starting point solution may affect the results of the optimization process. The user can provide an initial guess or choose between the following options: - **Binary Models**: - *random*: random numbers in (0, 1); - *uniform*: uniform initial guess in (0, 1); - - *degrees*: initial guess of each node is proportianal to its degree; + - *degrees*: initial guess of each node is proportional to its degree; - *degrees_minor*: initial guess of each node is inversely proportional to its degree; - *chung_lu*: initial guess given by Chung-Lu formula; @@ -2424,13 +2424,13 @@ def solve_tool( - *random*: random numbers in (0, 1); - *uniform*: uniform initial guess in (0, 1); - - *strengths*: initial guess of each node is proportianal to its stength; + - *strengths*: initial guess of each node is proportional to its strength; - *strengths_minor*: initial guess of each node is inversely proportional to its strength; :type initial_guess: str, optional :param adjacency: Adjacency can be a binary method (defaults is *dcm_exp*) or an adjacency matrix. :type adjacency: str or numpy.ndarray, optional - :param method_adjacency: If adjacency is a *model*, it is the *methdod* used to solve it. Defaults to "newton". + :param method_adjacency: If adjacency is a *model*, it is the *method* used to solve it. Defaults to "newton". :type method_adjacency: str, optional :param initial_guess_adjacency: If adjacency is a *model*, it is the chosen initial guess. Defaults to "random". :type initial_guess_adjacency: str, optional @@ -2442,12 +2442,12 @@ def solve_tool( :type verbose: bool, optional :param linsearch: If True the linsearch function is active, defaults to True. :type linsearch: bool, optional - :param tol: parameter controlling the tollerance of the norm the gradient function, defaults to 1e-8. + :param tol: parameter controlling the tolerance of the norm the gradient function, defaults to 1e-8. :type tol: float, optional - :param eps: parameter controlling the tollerance of the difference between two iterations, defaults to 1e-8. + :param eps: parameter controlling the tolerance of the difference between two iterations, defaults to 1e-8. :type eps: float, optional """ - # TODO: aggiungere tutti i metodi + # TODO: add all methods if model in ["dcm", "dcm_exp", "decm", "decm_exp"]: self._solve_problem( initial_guess=initial_guess, @@ -2480,9 +2480,9 @@ def solve_tool( print("\nmin eig = {}".format(self.error)) def ensemble_sampler(self, n, cpu_n=1, output_dir="sample/", seed=42): - """The function sample a given number of graphs in the ensemble - generated from the last model solved. Each grpah is an edgelist - written in the output directory as `.txt` file. + """The function samples a given number of graphs in the ensemble + generated from the last model solved. Each graph is an edgelist + written in the output directory as a `.txt` file. The function is parallelised and can run on multiple cpus. :param n: Number of graphs to sample. @@ -2682,6 +2682,7 @@ def __init__(self, biadjacency=None, adjacency_list=None, edgelist=None, degree_ self.full_rows_num = None self.full_rows_num = None self.solution_converged = None + self.progress_bar = None def _initialize_graph(self, biadjacency=None, adjacency_list=None, edgelist=None, degree_sequences=None): """ @@ -3571,7 +3572,7 @@ def _pvals_validator(self, pval_list, alpha=0.05): multiplier = 2 * alpha / (self.n_cols * (self.n_cols - 1)) try: eff_fdr_pos = np.where(sorted_pvals <= (np.arange(1, len(sorted_pvals) + 1) * alpha * multiplier))[0][-1] - except: + except IndexError: print('No V-motifs will be validated. Try increasing alpha') eff_fdr_pos = 0 eff_fdr_th = eff_fdr_pos * multiplier diff --git a/src/NEMtropy/models_functions.py b/src/NEMtropy/models_functions.py index 0aa24aa9..8a1db280 100644 --- a/src/NEMtropy/models_functions.py +++ b/src/NEMtropy/models_functions.py @@ -174,27 +174,26 @@ def pmatrix_cm(x, args): @jit(nopython=True) -def linsearch_fun_CM(X, args): +def linsearch_fun_CM(xx, args): """Linsearch function for UBCM newton and quasinewton methods. The function returns the step's size, alpha. Alpha determines how much to move on the descending direction found by the algorithm. - :param X: Tuple of arguments to find alpha: + :param xx: Tuple of arguments to find alpha: solution, solution step, tuning parameter beta, initial alpha, function f - :type X: (numpy.ndarray, numpy.ndarray, float, float, func) + :type xx: (numpy.ndarray, numpy.ndarray, float, float, func) :param args: Tuple, step function and arguments. :type args: (func, tuple) :return: Working alpha. :rtype: float """ - # TODO: change X to xx - x = X[0] - dx = X[1] - beta = X[2] - alfa = X[3] - f = X[4] + x = xx[0] + dx = xx[1] + beta = xx[2] + alfa = xx[3] + f = xx[4] step_fun = args[0] arg_step_fun = args[1] @@ -219,26 +218,25 @@ def linsearch_fun_CM(X, args): @jit(nopython=True) -def linsearch_fun_CM_fixed(X): +def linsearch_fun_CM_fixed(xx): """Linsearch function for UBCM fixed-point method. The function returns the step's size, alpha. Alpha determines how much to move on the descending direction found by the algorithm. - :param X: Tuple of arguments to find alpha: + :param xx: Tuple of arguments to find alpha: solution, solution step, tuning parameter beta, initial alpha, step. - :type X: (numpy.ndarray, numpy.ndarray, float, float, int) + :type xx: (numpy.ndarray, numpy.ndarray, float, float, int) :return: Working alpha. :rtype: float """ - # TODO: change X to xx - x = X[0] - dx = X[1] - dx_old = X[2] - alfa = X[3] - beta = X[4] - step = X[5] + x = xx[0] + dx = xx[1] + dx_old = xx[2] + alfa = xx[3] + beta = xx[4] + step = xx[5] eps2 = 1e-2 alfa0 = (eps2 - 1) * x / dx @@ -439,28 +437,27 @@ def loglikelihood_hessian_diag_cm_exp(x, args): @jit(nopython=True) -def linsearch_fun_CM_exp(X, args): +def linsearch_fun_CM_exp(xx, args): """Linsearch function for UBCM newton and quasinewton methods. The function returns the step's size, alpha. Alpha determines how much to move on the descending direction found by the algorithm. This function works on UBCM exponential version. - :param X: Tuple of arguments to find alpha: + :param xx: Tuple of arguments to find alpha: solution, solution step, tuning parameter beta, initial alpha, function f - :type X: (numpy.ndarray, numpy.ndarray, float, float, func) + :type xx: (numpy.ndarray, numpy.ndarray, float, float, func) :param args: Tuple, step function and arguments. :type args: (func, tuple) :return: Working alpha. :rtype: float """ - # TODO: change X to xx - x = X[0] - dx = X[1] - beta = X[2] - alfa = X[3] - f = X[4] + x = xx[0] + dx = xx[1] + beta = xx[2] + alfa = xx[3] + f = xx[4] step_fun = args[0] arg_step_fun = args[1] @@ -480,26 +477,25 @@ def linsearch_fun_CM_exp(X, args): @jit(nopython=True) -def linsearch_fun_CM_exp_fixed(X): +def linsearch_fun_CM_exp_fixed(xx): """Linsearch function for UBCM fixed-point method. The function returns the step's size, alpha. Alpha determines how much to move on the descending direction found by the algorithm. This function works on UBCM exponential version. - :param X: Tuple of arguments to find alpha: + :param xx: Tuple of arguments to find alpha: solution, solution step, tuning parameter beta, initial alpha, step. - :type X: (numpy.ndarray, numpy.ndarray, float, float, int) + :type xx: (numpy.ndarray, numpy.ndarray, float, float, int) :return: Working alpha. :rtype: float """ - # TODO: change X to xx - dx = X[1] - dx_old = X[2] - alfa = X[3] - beta = X[4] - step = X[5] + dx = xx[1] + dx_old = xx[2] + alfa = xx[3] + beta = xx[4] + step = xx[5] if step: kk = 0 @@ -736,27 +732,26 @@ def loglikelihood_hessian_diag_ecm(sol, args): @jit(nopython=True) -def linsearch_fun_ECM(X, args): +def linsearch_fun_ECM(xx, args): """Linsearch function for UECM newton and quasinewton methods. The function returns the step's size, alpha. Alpha determines how much to move on the descending direction found by the algorithm. - :param X: Tuple of arguments to find alpha: + :param xx: Tuple of arguments to find alpha: solution, solution step, tuning parameter beta, initial alpha, function f - :type X: (numpy.ndarray, numpy.ndarray, float, float, func) + :type xx: (numpy.ndarray, numpy.ndarray, float, float, func) :param args: Tuple, step function and arguments. :type args: (func, tuple) :return: Working alpha. :rtype: float """ - # TODO: change X to xx - x = X[0] - dx = X[1] - beta = X[2] - alfa = X[3] - f = X[4] + x = xx[0] + dx = xx[1] + beta = xx[2] + alfa = xx[3] + f = xx[4] step_fun = args[0] arg_step_fun = args[1] @@ -791,26 +786,25 @@ def linsearch_fun_ECM(X, args): @jit(nopython=True) -def linsearch_fun_ECM_fixed(X): +def linsearch_fun_ECM_fixed(xx): """Linsearch function for UECM fixed-point method. The function returns the step's size, alpha. Alpha determines how much to move on the descending direction found by the algorithm. - :param X: Tuple of arguments to find alpha: + :param xx: Tuple of arguments to find alpha: solution, solution step, tuning parameter beta, initial alpha, step. - :type X: (numpy.ndarray, numpy.ndarray, float, float, int) + :type xx: (numpy.ndarray, numpy.ndarray, float, float, int) :return: Working alpha. :rtype: float """ - # TODO: change X to xx - x = X[0] - dx = X[1] - dx_old = X[2] - alfa = X[3] - beta = X[4] - step = X[5] + x = xx[0] + dx = xx[1] + dx_old = xx[2] + alfa = xx[3] + beta = xx[4] + step = xx[5] eps2 = 1e-2 alfa0 = (eps2 - 1) * x / dx @@ -1083,28 +1077,27 @@ def loglikelihood_hessian_diag_ecm_exp(sol, args): @jit(nopython=True) -def linsearch_fun_ECM_exp(X, args): +def linsearch_fun_ECM_exp(xx, args): """Linsearch function for UECM newton and quasinewton methods. The function returns the step's size, alpha. Alpha determines how much to move on the descending direction found by the algorithm. This function works on UBCM exponential version. - :param X: Tuple of arguments to find alpha: + :param xx: Tuple of arguments to find alpha: solution, solution step, tuning parameter beta, initial alpha, function f - :type X: (numpy.ndarray, numpy.ndarray, float, float, func) + :type xx: (numpy.ndarray, numpy.ndarray, float, float, func) :param args: Tuple, step function and arguments. :type args: (func, tuple) :return: Working alpha. :rtype: float """ - # TODO: change X to xx - x = X[0] - dx = X[1] - beta = X[2] - alfa = X[3] - f = X[4] + x = xx[0] + dx = xx[1] + beta = xx[2] + alfa = xx[3] + f = xx[4] step_fun = args[0] arg_step_fun = args[1] @@ -1136,27 +1129,26 @@ def linsearch_fun_ECM_exp(X, args): @jit(nopython=True) -def linsearch_fun_ECM_exp_fixed(X): +def linsearch_fun_ECM_exp_fixed(xx): """Linsearch function for UECM fixed-point method. The function returns the step's size, alpha. Alpha determines how much to move on the descending direction found by the algorithm. This function works on UBCM exponential version. - :param X: Tuple of arguments to find alpha: + :param xx: Tuple of arguments to find alpha: solution, solution step, tuning parameter beta, initial alpha, step. - :type X: (numpy.ndarray, numpy.ndarray, float, float, int) + :type xx: (numpy.ndarray, numpy.ndarray, float, float, int) :return: Working alpha. :rtype: float """ - # TODO: change X to xx - x = X[0] - dx = X[1] - dx_old = X[2] - alfa = X[3] - beta = X[4] - step = X[5] + x = xx[0] + dx = xx[1] + dx_old = xx[2] + alfa = xx[3] + beta = xx[4] + step = xx[5] nnn = int(len(x) / 2) while True: @@ -1538,28 +1530,27 @@ def loglikelihood_hessian_diag_crema_sparse_2(beta, args): @jit(nopython=True) -def linsearch_fun_crema_undirected(X, args): +def linsearch_fun_crema_undirected(xx, args): """Linsearch function for CReMa newton and quasinewton methods. The function returns the step's size, alpha. Alpha determines how much to move on the descending direction found by the algorithm. - :param X: Tuple of arguments to find alpha: + :param xx: Tuple of arguments to find alpha: solution, solution step, tuning parameter beta, initial alpha, function f - :type X: (numpy.ndarray, numpy.ndarray, + :type xx: (numpy.ndarray, numpy.ndarray, float, float, func) :param args: Tuple, step function and arguments. :type args: (func, tuple) :return: Working alpha. :rtype: float """ - # TODO: change X to xx - x = X[0] - dx = X[1] - beta = X[2] - alfa = X[3] - f = X[4] + x = xx[0] + dx = xx[1] + beta = xx[2] + alfa = xx[3] + f = xx[4] step_fun = args[0] arg_step_fun = args[1] @@ -1580,25 +1571,24 @@ def linsearch_fun_crema_undirected(X, args): @jit(nopython=True) -def linsearch_fun_crema_undirected_fixed(X): +def linsearch_fun_crema_undirected_fixed(xx): """Linsearch function for CReMa fixed-point method. The function returns the step's size, alpha. Alpha determines how much to move on the descending direction found by the algorithm. - :param X: Tuple of arguments to find alpha: + :param xx: Tuple of arguments to find alpha: solution, solution step, tuning parameter beta, initial alpha, step. - :type X: (numpy.ndarray, numpy.ndarray, float, float, int) + :type xx: (numpy.ndarray, numpy.ndarray, float, float, int) :return: Working alpha. :rtype: float """ - # TODO: change X to xx - dx = X[1] - dx_old = X[2] - alfa = X[3] - beta = X[4] - step = X[5] + dx = xx[1] + dx_old = xx[2] + alfa = xx[3] + beta = xx[4] + step = xx[5] if step: kk = 0 @@ -3167,28 +3157,27 @@ def expected_in_degree_dcm_exp(theta): @jit(nopython=True) -def linsearch_fun_DCM_exp(X, args): +def linsearch_fun_DCM_exp(xx, args): """Linsearch function for DBCM newton and quasinewton methods. The function returns the step's size, alpha. Alpha determines how much to move on the descending direction found by the algorithm. This function works on DBCM exponential version. - :param X: Tuple of arguments to find alpha: + :param xx: Tuple of arguments to find alpha: solution, solution step, tuning parameter beta, initial alpha, function f - :type X: (numpy.ndarray, numpy.ndarray, float, float, func) + :type xx: (numpy.ndarray, numpy.ndarray, float, float, func) :param args: Tuple, step function and arguments. :type args: (func, tuple) :return: Working alpha. :rtype: float """ - # TODO: change X to xx - x = X[0] - dx = X[1] - beta = X[2] - alfa = X[3] - f = X[4] + x = xx[0] + dx = xx[1] + beta = xx[2] + alfa = xx[3] + f = xx[4] step_fun = args[0] arg_step_fun = args[1] @@ -3207,26 +3196,25 @@ def linsearch_fun_DCM_exp(X, args): @jit(nopython=True) -def linsearch_fun_DCM_exp_fixed(X): +def linsearch_fun_DCM_exp_fixed(xx): """Linsearch function for DBCM fixed-point method. The function returns the step's size, alpha. Alpha determines how much to move on the descending direction found by the algorithm. This function works on DBCM exponential version. - :param X: Tuple of arguments to find alpha: + :param xx: Tuple of arguments to find alpha: solution, solution step, tuning parameter beta, initial alpha, step. - :type X: (numpy.ndarray, numpy.ndarray, float, float, int) + :type xx: (numpy.ndarray, numpy.ndarray, float, float, int) :return: Working alpha. :rtype: float """ - # TODO: change X to xx - dx = X[1] - dx_old = X[2] - alfa = X[3] - beta = X[4] - step = X[5] + dx = xx[1] + dx_old = xx[2] + alfa = xx[3] + beta = xx[4] + step = xx[5] if step: kk = 0 @@ -3712,28 +3700,27 @@ def expected_decm_exp(theta): @jit(nopython=True) -def linsearch_fun_DECM_exp(X, args): +def linsearch_fun_DECM_exp(xx, args): """Linsearch function for DECM newton and quasinewton methods. The function returns the step's size, alpha. Alpha determines how much to move on the descending direction found by the algorithm. This function works on DECM exponential version. - :param X: Tuple of arguments to find alpha: + :param xx: Tuple of arguments to find alpha: solution, solution step, tuning parameter beta, initial alpha, function f - :type X: (numpy.ndarray, numpy.ndarray, float, float, func) + :type xx: (numpy.ndarray, numpy.ndarray, float, float, func) :param args: Tuple, step function and arguments. :type args: (func, tuple) :return: Working alpha. :rtype: float """ - # TODO: change X to xx - x = X[0] - dx = X[1] - beta = X[2] - alfa = X[3] - f = X[4] + x = xx[0] + dx = xx[1] + beta = xx[2] + alfa = xx[3] + f = xx[4] step_fun = args[0] arg_step_fun = args[1] @@ -3771,27 +3758,26 @@ def linsearch_fun_DECM_exp(X, args): @jit(nopython=True) -def linsearch_fun_DECM_exp_fixed(X): +def linsearch_fun_DECM_exp_fixed(xx): """Linsearch function for DECM fixed-point method. The function returns the step's size, alpha. Alpha determines how much to move on the descending direction found by the algorithm. This function works on DECM exponential version. - :param X: Tuple of arguments to find alpha: + :param xx: Tuple of arguments to find alpha: solution, solution step, tuning parameter beta, initial alpha, step. - :type X: (numpy.ndarray, numpy.ndarray, float, float, int) + :type xx: (numpy.ndarray, numpy.ndarray, float, float, int) :return: Working alpha. :rtype: float """ - # TODO: change X to xx - x = X[0] - dx = X[1] - dx_old = X[2] - alfa = X[3] - beta = X[4] - step = X[5] + x = xx[0] + dx = xx[1] + dx_old = xx[2] + alfa = xx[3] + beta = xx[4] + step = xx[5] # Mettere il check sulle y nnn = int(len(x) / 4) @@ -3808,7 +3794,7 @@ def linsearch_fun_DECM_exp_fixed(X): + alfa * dx[2 * nnn: 3 * nnn][ind_yout]) + \ (x[3 * nnn:][ind_yin] + alfa * dx[3 * nnn:][ind_yin]) - if (cond) > tmp * 0.01: + if cond > tmp * 0.01: break else: alfa *= beta @@ -4448,13 +4434,13 @@ def expected_out_strength_crema_directed_sparse(sol, adj): @jit(nopython=True) -def expected_in_stregth_crema_directed(sol, adj): +def expected_in_strength_crema_directed(sol, adj): """Expected in-strength after CReMa. :param sol: CReMa solution :type sol: numpy.ndarray :param adj: Tuple containing the original topology edges list - and link related weigths. + and link related weights. :type adj: (numpy.ndarray, numpy.ndarray, numpy.ndarray) :return: Expected in-strength sequence :rtype: numpy.ndarray @@ -4476,12 +4462,12 @@ def expected_in_stregth_crema_directed(sol, adj): @jit(nopython=True) def expected_in_stregth_crema_directed_sparse(sol, adj): - """Expected in-strength after CReMa. Sparse inisialization version. + """Expected in-strength after CReMa. Sparse initialization version. :param sol: CReMa solution :type sol: numpy.ndarray :param adj: Tuple containing the original topology edges list - and link related weigths. + and link related weights. :type adj: (numpy.ndarray, numpy.ndarray, numpy.ndarray) :return: Expected in-strength sequence :rtype: numpy.ndarray From 117f3d606a88ee8fcd8164bbdf2c70d0278ac278 Mon Sep 17 00:00:00 2001 From: EmilianoMarchese Date: Tue, 16 Mar 2021 11:03:38 +0100 Subject: [PATCH 4/4] v2.0.3: Fixed typos. Update README and examples to the latest version. --- README.md | 14 +++--- examples/Directed Graphs.ipynb | 78 +++++++++++++++---------------- examples/Undirected Graphs.ipynb | 79 ++++++++++++-------------------- requirements.txt | 19 ++++++++ 4 files changed, 94 insertions(+), 96 deletions(-) diff --git a/README.md b/README.md index 2d7ab0f0..5db8fe52 100644 --- a/README.md +++ b/README.md @@ -143,13 +143,13 @@ adjacency matrix by running the NEMtropy build graph function. from NEMtropy.network_functions import build_graph_from_edgelist edgelist_ens = np.loadtxt("sample/0.txt") - ens_adj = build_graph_from_edgelist(edgelist = edgelist_ens, - is_directed = False, - is_sparse = False, - is_weighted = False) + ens_adj = build_adjacency_from_edgelist(edgelist = edgelist_ens, + is_directed = False, + is_sparse = False, + is_weighted = False) ``` -These collection of random adjacency matrices can be used as a null model: +These collections of random adjacency matrices can be used as a null model: it is enough to compute the expected value of the selected network feature on the ensemble of matrices and to compare it with its original value. @@ -162,7 +162,7 @@ You can find complete documentation about NEMtropy library in [docs](https://nem Development ----------- Please work on a feature branch and create a pull request to the development -branch. If necessary to merge manually do so without fast forward: +branch. If necessary to merge manually do so without fast-forward: ``` $ git merge --no-ff myfeature @@ -179,7 +179,7 @@ To build a development environment run: Testing ------- If you want to test the package integrity, you can run the following -bash command from the tests directory: +bash command from the tests' directory: ``` $ bash run_all.sh diff --git a/examples/Directed Graphs.ipynb b/examples/Directed Graphs.ipynb index 621f5862..f00c91c0 100644 --- a/examples/Directed Graphs.ipynb +++ b/examples/Directed Graphs.ipynb @@ -2,7 +2,7 @@ "cells": [ { "source": [ - "The aim of the notebook is to help you to take confidence with the directed graph class of NEMtropy. We will guide you through the functionality of the module and they can be use to reconstruct a network given partial information or to generate a null model given a certain network.\n", + "The aim of the notebook is to help you to take confidence with the directed graph class of NEMtropy. We will guide you through the functionality of the module, how they can be use to reconstruct a network given partial information or to generate a null model given a certain network.\n", "For more detail about the theory behind this, we suggest you to read ....." ], "cell_type": "markdown", @@ -18,7 +18,7 @@ "import networkx as nx\n", "from NEMtropy import DirectedGraph\n", "from NEMtropy import matrix_generator as mg\n", - "from NEMtropy.network_functions import build_graph_from_edgelist\n" + "from NEMtropy.network_functions import build_adjacency_from_edgelist\n" ] }, { @@ -59,7 +59,7 @@ }, { "source": [ - "DirectedGraph instance can be initialised using adjacency matrix, edgelist or degree and strength sequences. As an example we initialise a graph instance using the adjacency matrix." + "DirectedGraph instance can be initialised using adjacency matrix, edgelist or degree and strength sequences. As an example, we initialise a graph instance using the adjacency matrix." ], "cell_type": "markdown", "metadata": {} @@ -75,7 +75,7 @@ }, { "source": [ - "When you use an initiliasition instance that is not the adjacency matrix you must specify what you are passing to DirectedGraph. As an exaple we can use the edgelist." + "When the initiliasition instance is not an adjacency matrix, you must specify what you are passing to DirectedGraph: for exaple we can use an edgelist." ], "cell_type": "markdown", "metadata": {} @@ -173,10 +173,10 @@ "edgelist_ens = np.loadtxt(\"sample/0.txt\")\n", "\n", "# and build the adjacency matrix\n", - "ens_adj = build_graph_from_edgelist(edgelist = edgelist_ens,\n", - " is_directed = True,\n", - " is_sparse = False,\n", - " is_weighted = False)" + "ens_adj = build_adjacency_from_edgelist(edgelist = edgelist_ens,\n", + " is_directed = True,\n", + " is_sparse = False,\n", + " is_weighted = False)" ] }, { @@ -188,7 +188,7 @@ }, { "source": [ - "Suppose now that you have only partial information about yournetwork: only the degree sequence is disclosed. Following the same procedure described above, we can generate generate a network satisfying (on average) the observed degree sequence." + "Suppose now that you have only partial information about yournetwork: only the degree sequence is disclosed. Following the same procedure described above, we can generate a network satisfying (on average) the observed degree sequence." ], "cell_type": "markdown", "metadata": {} @@ -216,10 +216,10 @@ "edgelist_ens = np.loadtxt(\"sample/0.txt\")\n", "\n", "# then we can build the adjacency matrix\n", - "ens_adj = build_graph_from_edgelist(edgelist = edgelist_ens,\n", - " is_directed = True,\n", - " is_sparse = False,\n", - " is_weighted = False)\n", + "ens_adj = build_adjacency_from_edgelist(edgelist = edgelist_ens,\n", + " is_directed = True,\n", + " is_sparse = False,\n", + " is_weighted = False)\n", "\n", "# or generate a networkx graph from it\n", "G = nx.DiGraph()\n", @@ -297,18 +297,18 @@ { "output_type": "error", "ename": "TypingError", - "evalue": "Failed in nopython mode pipeline (step: nopython frontend)\n\u001b[1m\u001b[1m\u001b[1mNo implementation of function Function() found for signature:\n \n >>> len(none)\n \nThere are 16 candidate implementations:\n\u001b[1m - Of which 16 did not match due to:\n Overload of function 'len': File: : Line N/A.\n With argument(s): '(none)':\u001b[0m\n\u001b[1m No match.\u001b[0m\n\u001b[0m\n\u001b[0m\u001b[1mDuring: resolving callee type: Function()\u001b[0m\n\u001b[0m\u001b[1mDuring: typing of call at /Users/emiliano/Documents/PhD/Directed_Class_Master/classes/src/NEMtropy/models_functions.py (4100)\n\u001b[0m\n\u001b[1m\nFile \"../src/NEMtropy/models_functions.py\", line 4100:\u001b[0m\n\u001b[1mdef loglikelihood_prime_crema_directed(beta, args):\n \n\n\u001b[1m aux_n = len(s_out)\n\u001b[0m \u001b[1m^\u001b[0m\u001b[0m\n", + "evalue": "Failed in nopython mode pipeline (step: nopython frontend)\n\u001B[1m\u001B[1m\u001B[1mNo implementation of function Function() found for signature:\n \n >>> len(none)\n \nThere are 16 candidate implementations:\n\u001B[1m - Of which 16 did not match due to:\n Overload of function 'len': File: : Line N/A.\n With argument(s): '(none)':\u001B[0m\n\u001B[1m No match.\u001B[0m\n\u001B[0m\n\u001B[0m\u001B[1mDuring: resolving callee type: Function()\u001B[0m\n\u001B[0m\u001B[1mDuring: typing of call at /Users/emiliano/Documents/PhD/Directed_Class_Master/classes/src/NEMtropy/models_functions.py (4100)\n\u001B[0m\n\u001B[1m\nFile \"../src/NEMtropy/models_functions.py\", line 4100:\u001B[0m\n\u001B[1mdef loglikelihood_prime_crema_directed(beta, args):\n \n\n\u001B[1m aux_n = len(s_out)\n\u001B[0m \u001B[1m^\u001B[0m\u001B[0m\n", "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mTypingError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[0minitial_guess\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m\"random\"\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 5\u001b[0m \u001b[0madjacency\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m\"dcm_exp\"\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 6\u001b[0;31m method_adjacency=\"newton\")\n\u001b[0m\u001b[1;32m 7\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 8\u001b[0m \u001b[0;31m# The optimisation problem is divided in two steps: firstly the binary model is solved and its solution are one\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/Documents/PhD/Directed_Class_Master/classes/src/NEMtropy/graph_classes.py\u001b[0m in \u001b[0;36msolve_tool\u001b[0;34m(self, model, method, initial_guess, adjacency, method_adjacency, initial_guess_adjacency, max_steps, full_return, verbose, linsearch, tol, eps)\u001b[0m\n\u001b[1;32m 2469\u001b[0m \u001b[0mlinsearch\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mlinsearch\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2470\u001b[0m \u001b[0mtol\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mtol\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 2471\u001b[0;31m \u001b[0meps\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0meps\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 2472\u001b[0m )\n\u001b[1;32m 2473\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_solution_error\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/Documents/PhD/Directed_Class_Master/classes/src/NEMtropy/graph_classes.py\u001b[0m in \u001b[0;36m_solve_problem_crema_directed\u001b[0;34m(self, initial_guess, model, adjacency, method, method_adjacency, initial_guess_adjacency, max_steps, tol, eps, full_return, verbose, linsearch, regularise, regularise_eps)\u001b[0m\n\u001b[1;32m 2360\u001b[0m \u001b[0mregularise_eps\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mregularise_eps\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2361\u001b[0m \u001b[0mlinsearch\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mlinsearch\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 2362\u001b[0;31m \u001b[0mfull_return\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mfull_return\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 2363\u001b[0m )\n\u001b[1;32m 2364\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/Documents/PhD/Directed_Class_Master/classes/src/NEMtropy/solver_functions.py\u001b[0m in \u001b[0;36msolver\u001b[0;34m(x0, fun, step_fun, linsearch_fun, hessian_regulariser, fun_jac, tol, eps, max_steps, method, verbose, regularise, regularise_eps, full_return, linsearch)\u001b[0m\n\u001b[1;32m 146\u001b[0m \u001b[0mx\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mx0\u001b[0m \u001b[0;31m# initial point\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 147\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 148\u001b[0;31m \u001b[0mf\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mfun\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 149\u001b[0m \u001b[0mnorm\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mlinalg\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mnorm\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mf\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 150\u001b[0m \u001b[0mdiff\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/Documents/PhD/Directed_Class_Master/classes/src/NEMtropy/graph_classes.py\u001b[0m in \u001b[0;36m\u001b[0;34m(x)\u001b[0m\n\u001b[1;32m 2027\u001b[0m \"crema-newton\": lambda x: -mof.loglikelihood_prime_crema_directed(\n\u001b[1;32m 2028\u001b[0m \u001b[0mx\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 2029\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 2030\u001b[0m ),\n\u001b[1;32m 2031\u001b[0m \"crema-quasinewton\": lambda x: -mof.loglikelihood_prime_crema_directed(\n", - "\u001b[0;32m/usr/local/lib/python3.7/site-packages/numba/core/dispatcher.py\u001b[0m in \u001b[0;36m_compile_for_args\u001b[0;34m(self, *args, **kws)\u001b[0m\n\u001b[1;32m 412\u001b[0m \u001b[0me\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mpatch_message\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmsg\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 413\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 414\u001b[0;31m \u001b[0merror_rewrite\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0me\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'typing'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 415\u001b[0m \u001b[0;32mexcept\u001b[0m \u001b[0merrors\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mUnsupportedError\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0me\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 416\u001b[0m \u001b[0;31m# Something unsupported is present in the user code, add help info\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/usr/local/lib/python3.7/site-packages/numba/core/dispatcher.py\u001b[0m in \u001b[0;36merror_rewrite\u001b[0;34m(e, issue_type)\u001b[0m\n\u001b[1;32m 355\u001b[0m \u001b[0;32mraise\u001b[0m \u001b[0me\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 356\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 357\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0me\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mwith_traceback\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;32mNone\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 358\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 359\u001b[0m \u001b[0margtypes\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;31mTypingError\u001b[0m: Failed in nopython mode pipeline (step: nopython frontend)\n\u001b[1m\u001b[1m\u001b[1mNo implementation of function Function() found for signature:\n \n >>> len(none)\n \nThere are 16 candidate implementations:\n\u001b[1m - Of which 16 did not match due to:\n Overload of function 'len': File: : Line N/A.\n With argument(s): '(none)':\u001b[0m\n\u001b[1m No match.\u001b[0m\n\u001b[0m\n\u001b[0m\u001b[1mDuring: resolving callee type: Function()\u001b[0m\n\u001b[0m\u001b[1mDuring: typing of call at /Users/emiliano/Documents/PhD/Directed_Class_Master/classes/src/NEMtropy/models_functions.py (4100)\n\u001b[0m\n\u001b[1m\nFile \"../src/NEMtropy/models_functions.py\", line 4100:\u001b[0m\n\u001b[1mdef loglikelihood_prime_crema_directed(beta, args):\n \n\n\u001b[1m aux_n = len(s_out)\n\u001b[0m \u001b[1m^\u001b[0m\u001b[0m\n" + "\u001B[0;31m---------------------------------------------------------------------------\u001B[0m", + "\u001B[0;31mTypingError\u001B[0m Traceback (most recent call last)", + "\u001B[0;32m\u001B[0m in \u001B[0;36m\u001B[0;34m\u001B[0m\n\u001B[1;32m 4\u001B[0m \u001B[0minitial_guess\u001B[0m\u001B[0;34m=\u001B[0m\u001B[0;34m\"random\"\u001B[0m\u001B[0;34m,\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m 5\u001B[0m \u001B[0madjacency\u001B[0m\u001B[0;34m=\u001B[0m\u001B[0;34m\"dcm_exp\"\u001B[0m\u001B[0;34m,\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0;32m----> 6\u001B[0;31m method_adjacency=\"newton\")\n\u001B[0m\u001B[1;32m 7\u001B[0m \u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m 8\u001B[0m \u001B[0;31m# The optimisation problem is divided in two steps: firstly the binary model is solved and its solution are one\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n", + "\u001B[0;32m~/Documents/PhD/Directed_Class_Master/classes/src/NEMtropy/graph_classes.py\u001B[0m in \u001B[0;36msolve_tool\u001B[0;34m(self, model, method, initial_guess, adjacency, method_adjacency, initial_guess_adjacency, max_steps, full_return, verbose, linsearch, tol, eps)\u001B[0m\n\u001B[1;32m 2469\u001B[0m \u001B[0mlinsearch\u001B[0m\u001B[0;34m=\u001B[0m\u001B[0mlinsearch\u001B[0m\u001B[0;34m,\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m 2470\u001B[0m \u001B[0mtol\u001B[0m\u001B[0;34m=\u001B[0m\u001B[0mtol\u001B[0m\u001B[0;34m,\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0;32m-> 2471\u001B[0;31m \u001B[0meps\u001B[0m\u001B[0;34m=\u001B[0m\u001B[0meps\u001B[0m\u001B[0;34m,\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0m\u001B[1;32m 2472\u001B[0m )\n\u001B[1;32m 2473\u001B[0m \u001B[0mself\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0m_solution_error\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n", + "\u001B[0;32m~/Documents/PhD/Directed_Class_Master/classes/src/NEMtropy/graph_classes.py\u001B[0m in \u001B[0;36m_solve_problem_crema_directed\u001B[0;34m(self, initial_guess, model, adjacency, method, method_adjacency, initial_guess_adjacency, max_steps, tol, eps, full_return, verbose, linsearch, regularise, regularise_eps)\u001B[0m\n\u001B[1;32m 2360\u001B[0m \u001B[0mregularise_eps\u001B[0m\u001B[0;34m=\u001B[0m\u001B[0mregularise_eps\u001B[0m\u001B[0;34m,\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m 2361\u001B[0m \u001B[0mlinsearch\u001B[0m\u001B[0;34m=\u001B[0m\u001B[0mlinsearch\u001B[0m\u001B[0;34m,\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0;32m-> 2362\u001B[0;31m \u001B[0mfull_return\u001B[0m\u001B[0;34m=\u001B[0m\u001B[0mfull_return\u001B[0m\u001B[0;34m,\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0m\u001B[1;32m 2363\u001B[0m )\n\u001B[1;32m 2364\u001B[0m \u001B[0;34m\u001B[0m\u001B[0m\n", + "\u001B[0;32m~/Documents/PhD/Directed_Class_Master/classes/src/NEMtropy/solver_functions.py\u001B[0m in \u001B[0;36msolver\u001B[0;34m(x0, fun, step_fun, linsearch_fun, hessian_regulariser, fun_jac, tol, eps, max_steps, method, verbose, regularise, regularise_eps, full_return, linsearch)\u001B[0m\n\u001B[1;32m 146\u001B[0m \u001B[0mx\u001B[0m \u001B[0;34m=\u001B[0m \u001B[0mx0\u001B[0m \u001B[0;31m# initial point\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m 147\u001B[0m \u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0;32m--> 148\u001B[0;31m \u001B[0mf\u001B[0m \u001B[0;34m=\u001B[0m \u001B[0mfun\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0mx\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0m\u001B[1;32m 149\u001B[0m \u001B[0mnorm\u001B[0m \u001B[0;34m=\u001B[0m \u001B[0mnp\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mlinalg\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mnorm\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0mf\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m 150\u001B[0m \u001B[0mdiff\u001B[0m \u001B[0;34m=\u001B[0m \u001B[0;36m1\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n", + "\u001B[0;32m~/Documents/PhD/Directed_Class_Master/classes/src/NEMtropy/graph_classes.py\u001B[0m in \u001B[0;36m\u001B[0;34m(x)\u001B[0m\n\u001B[1;32m 2027\u001B[0m \"crema-newton\": lambda x: -mof.loglikelihood_prime_crema_directed(\n\u001B[1;32m 2028\u001B[0m \u001B[0mx\u001B[0m\u001B[0;34m,\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0;32m-> 2029\u001B[0;31m \u001B[0mself\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0margs\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0m\u001B[1;32m 2030\u001B[0m ),\n\u001B[1;32m 2031\u001B[0m \"crema-quasinewton\": lambda x: -mof.loglikelihood_prime_crema_directed(\n", + "\u001B[0;32m/usr/local/lib/python3.7/site-packages/numba/core/dispatcher.py\u001B[0m in \u001B[0;36m_compile_for_args\u001B[0;34m(self, *args, **kws)\u001B[0m\n\u001B[1;32m 412\u001B[0m \u001B[0me\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mpatch_message\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0mmsg\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m 413\u001B[0m \u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0;32m--> 414\u001B[0;31m \u001B[0merror_rewrite\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0me\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0;34m'typing'\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0m\u001B[1;32m 415\u001B[0m \u001B[0;32mexcept\u001B[0m \u001B[0merrors\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mUnsupportedError\u001B[0m \u001B[0;32mas\u001B[0m \u001B[0me\u001B[0m\u001B[0;34m:\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m 416\u001B[0m \u001B[0;31m# Something unsupported is present in the user code, add help info\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n", + "\u001B[0;32m/usr/local/lib/python3.7/site-packages/numba/core/dispatcher.py\u001B[0m in \u001B[0;36merror_rewrite\u001B[0;34m(e, issue_type)\u001B[0m\n\u001B[1;32m 355\u001B[0m \u001B[0;32mraise\u001B[0m \u001B[0me\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m 356\u001B[0m \u001B[0;32melse\u001B[0m\u001B[0;34m:\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0;32m--> 357\u001B[0;31m \u001B[0;32mraise\u001B[0m \u001B[0me\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mwith_traceback\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0;32mNone\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0m\u001B[1;32m 358\u001B[0m \u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m 359\u001B[0m \u001B[0margtypes\u001B[0m \u001B[0;34m=\u001B[0m \u001B[0;34m[\u001B[0m\u001B[0;34m]\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n", + "\u001B[0;31mTypingError\u001B[0m: Failed in nopython mode pipeline (step: nopython frontend)\n\u001B[1m\u001B[1m\u001B[1mNo implementation of function Function() found for signature:\n \n >>> len(none)\n \nThere are 16 candidate implementations:\n\u001B[1m - Of which 16 did not match due to:\n Overload of function 'len': File: : Line N/A.\n With argument(s): '(none)':\u001B[0m\n\u001B[1m No match.\u001B[0m\n\u001B[0m\n\u001B[0m\u001B[1mDuring: resolving callee type: Function()\u001B[0m\n\u001B[0m\u001B[1mDuring: typing of call at /Users/emiliano/Documents/PhD/Directed_Class_Master/classes/src/NEMtropy/models_functions.py (4100)\n\u001B[0m\n\u001B[1m\nFile \"../src/NEMtropy/models_functions.py\", line 4100:\u001B[0m\n\u001B[1mdef loglikelihood_prime_crema_directed(beta, args):\n \n\n\u001B[1m aux_n = len(s_out)\n\u001B[0m \u001B[1m^\u001B[0m\u001B[0m\n" ] } ], @@ -356,10 +356,10 @@ "edgelist_ens = np.loadtxt(\"sample/0.txt\")\n", "\n", "# and build the adjacency matrix\n", - "ens_adj = build_graph_from_edgelist(edgelist = edgelist_ens,\n", - " is_directed = True,\n", - " is_sparse = False,\n", - " is_weighted = True)" + "ens_adj = build_adjacency_from_edgelist(edgelist = edgelist_ens,\n", + " is_directed = True,\n", + " is_sparse = False,\n", + " is_weighted = True)" ] }, { @@ -372,10 +372,10 @@ { "source": [ "Suppose you have only partial information about the network, for example:\n", - " - The binary structure is well-known and you want to assign weights to network edges;\n", - " - Only marginals information are available: degree and strength sequences.\n", + " - The binary structure is well-known, and you want to assign weights to network edges;\n", + " - Only marginal information are available: degree and strength sequences.\n", "\n", - "You can use NEMtropy methods to reconstruct a network configuration that is consistent with the available informations." + "You can use NEMtropy methods to reconstruct a network configuration that is consistent with the available information." ], "cell_type": "markdown", "metadata": {} @@ -457,10 +457,10 @@ "edgelist_ens = np.loadtxt(\"sample/0.txt\")\n", "\n", "# then we can build the adjacency matrix\n", - "ens_adj = build_graph_from_edgelist(edgelist = edgelist_ens,\n", - " is_directed = True,\n", - " is_sparse = False,\n", - " is_weighted = True)\n", + "ens_adj = build_adjacency_from_edgelist(edgelist = edgelist_ens,\n", + " is_directed = True,\n", + " is_sparse = False,\n", + " is_weighted = True)\n", "\n", "# and generate a networkx graph from it\n", "G = nx.from_numpy_array(ens_adj)" @@ -567,10 +567,10 @@ "edgelist_ens = np.loadtxt(\"sample/0.txt\")\n", "\n", "# then we can build the adjacency matrix\n", - "ens_adj = build_graph_from_edgelist(edgelist = edgelist_ens,\n", - " is_directed = True,\n", - " is_sparse = False,\n", - " is_weighted = True)\n", + "ens_adj = build_adjacency_from_edgelist(edgelist = edgelist_ens,\n", + " is_directed = True,\n", + " is_sparse = False,\n", + " is_weighted = True)\n", "\n", "# and generate a networkx graph from it\n", "G = nx.from_numpy_array(ens_adj)" diff --git a/examples/Undirected Graphs.ipynb b/examples/Undirected Graphs.ipynb index c1e6250f..22c6c722 100644 --- a/examples/Undirected Graphs.ipynb +++ b/examples/Undirected Graphs.ipynb @@ -2,7 +2,7 @@ "cells": [ { "source": [ - "The aim of the notebook is to help you to take confidence with the undirected graph class of NEMtropy. We will guide you through the functionality of the module and they can be use to reconstruct a network given partial information or to generate a null model given a certain network.\n", + "The aim of the notebook is to help you to take confidence with the undirected graph class of NEMtropy. We will guide you through the functionality of the module, and how they can be use to reconstruct a network given partial information or to generate a null model given a certain network.\n", "For more detail about the theory behind this, we suggest you to read ....." ], "cell_type": "markdown", @@ -17,34 +17,13 @@ "import numpy as np\n", "import networkx as nx\n", "from NEMtropy import UndirectedGraph, matrix_generator\n", - "from NEMtropy.network_functions import build_graph_from_edgelist" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "output_type": "error", - "ename": "ModuleNotFoundError", - "evalue": "No module named 'nemtropy'", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mModuleNotFoundError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;32mimport\u001b[0m \u001b[0mnemtropy\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;31mModuleNotFoundError\u001b[0m: No module named 'nemtropy'" - ] - } - ], - "source": [ - "import nemtropy" + "from NEMtropy.network_functions import build_adjacency_from_edgelist" ] }, { "source": [ "According to the type of undirected graph, the available models can be divided in:\n", - " - models for binary network: \"cm\" and \"cm_exp\"\n", + " - models for binary networks: \"cm\" and \"cm_exp\"\n", " - models for weighted networks: \"crema\", \"ecm\" and \"ecm_exp\"\n", "\n", "We will make some examples about both classes of models, lets start from binary models." @@ -76,7 +55,7 @@ }, { "source": [ - "UndirectedGraph instance can be initialised using adjacency matrix, edgelist or degree and strength sequences. As an example we initialise a graph instance using the adjacency matrix." + "UndirectedGraph instance can be initialised using adjacency matrix, edgelist or degree and strength sequences. As an example, we initialise a graph instance using the adjacency matrix." ], "cell_type": "markdown", "metadata": {} @@ -92,7 +71,7 @@ }, { "source": [ - "When you use an initiliasition instance that isn't the adjacency matrix you must specify what you are passing to UndirectedGraph. As an exaple we can use the edgelist." + "When you use an initiliasition instance that isn't the adjacency matrix, you must specify what you are passing to UndirectedGraph: for exaple an edgelist." ], "cell_type": "markdown", "metadata": {} @@ -198,10 +177,10 @@ "edgelist_ens = np.loadtxt(\"sample/0.txt\")\n", "\n", "# and build the adjacency matrix\n", - "ens_adj = build_graph_from_edgelist(edgelist = edgelist_ens,\n", - " is_directed = False,\n", - " is_sparse = False,\n", - " is_weighted = False)" + "ens_adj = build_adjacency_from_edgelist(edgelist = edgelist_ens,\n", + " is_directed = False,\n", + " is_sparse = False,\n", + " is_weighted = False)" ] }, { @@ -220,7 +199,7 @@ }, { "source": [ - "Suppose now that you have only partial information about the zachary karate club network: only the degree sequence is disclosed. Following the same procedure described above, we can generate generate a network having (on average) the same observed degree sequence." + "Suppose now that you have only partial information about the zachary karate club network: only the degree sequence is disclosed. Following the same procedure described above, we can generate a network having (on average) the same observed degree sequence." ], "cell_type": "markdown", "metadata": {} @@ -250,10 +229,10 @@ "G.add_edges_from(edgelist_ens)\n", "\n", "# then we can build the adjacency matrix\n", - "ens_adj = build_graph_from_edgelist(edgelist = edgelist_ens,\n", - " is_directed = False,\n", - " is_sparse = False,\n", - " is_weighted = False)\n", + "ens_adj = build_adjacency_from_edgelist(edgelist = edgelist_ens,\n", + " is_directed = False,\n", + " is_sparse = False,\n", + " is_weighted = False)\n", "\n", "# or generate a networkx graph from it\n", "G = nx.DiGraph()\n", @@ -373,10 +352,10 @@ "edgelist_ens = np.loadtxt(\"sample/0.txt\")\n", "\n", "# and build the adjacency matrix\n", - "ens_adj = build_graph_from_edgelist(edgelist = edgelist_ens,\n", - " is_directed = False,\n", - " is_sparse = False,\n", - " is_weighted = True)" + "ens_adj = build_adjacency_from_edgelist(edgelist = edgelist_ens,\n", + " is_directed = False,\n", + " is_sparse = False,\n", + " is_weighted = True)" ] }, { @@ -389,10 +368,10 @@ { "source": [ "Suppose you have only partial information about the network, for example:\n", - " - The binary structure is well-known and you want to assign weights to network edges;\n", - " - Only marginals information are available: degree and strength sequences.\n", + " - The binary structure is well-known, and you want to assign weights to network edges;\n", + " - Only marginal information are available: degree and strength sequences.\n", "\n", - "You can use NEMtropy methods to reconstruct a network configuration that is consistent with the available informations." + "You can use NEMtropy methods to reconstruct a network configuration that is consistent with the available information." ], "cell_type": "markdown", "metadata": {} @@ -473,10 +452,10 @@ "edgelist_ens = np.loadtxt(\"sample/0.txt\")\n", "\n", "# then we can build the adjacency matrix\n", - "ens_adj = build_graph_from_edgelist(edgelist = edgelist_ens,\n", - " is_directed = False,\n", - " is_sparse = False,\n", - " is_weighted = True)\n", + "ens_adj = build_adjacency_from_edgelist(edgelist = edgelist_ens,\n", + " is_directed = False,\n", + " is_sparse = False,\n", + " is_weighted = True)\n", "\n", "# and generate a networkx graph from it\n", "G = nx.from_numpy_array(ens_adj)" @@ -580,10 +559,10 @@ "edgelist_ens = np.loadtxt(\"sample/0.txt\")\n", "\n", "# then we can build the adjacency matrix\n", - "ens_adj = build_graph_from_edgelist(edgelist = edgelist_ens,\n", - " is_directed = False,\n", - " is_sparse = False,\n", - " is_weighted = True)\n", + "ens_adj = build_adjacency_from_edgelist(edgelist = edgelist_ens,\n", + " is_directed = False,\n", + " is_sparse = False,\n", + " is_weighted = True)\n", "\n", "# and generate a networkx graph from it\n", "G = nx.from_numpy_array(ens_adj)" diff --git a/requirements.txt b/requirements.txt index e69de29b..4e49e6a2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -0,0 +1,19 @@ +numpy~=1.20.1 +scipy~=1.6.0 +networkx~=2.5 +powerlaw~=1.4.6 +numba~=0.52.0 +NEMtropy~=1.0.5 +setuptools~=51.1.2 +Pillow~=8.1.0 +pip~=20.3.3 +wheel~=0.36.2 +six~=1.15.0 +llvmlite~=0.35.0 +matplotlib~=3.3.4 +mpmath~=1.2.1 +python-dateutil~=2.8.1 +decorator~=4.4.2 +pyparsing~=2.4.7 +cycler~=0.10.0 +kiwisolver~=1.3.1 \ No newline at end of file