yanat.core

yanat.core.communicability(adjacency_matrix: ndarray, alpha: float = 1, normalize: bool = False) ndarray[source]

Computes the communicability of the network, with the option to be scaled by a decay rate factor ‘alpha’. The alpha factor modulates the decay rate of walks, with smaller values leading to quicker subsidence. Alpha should be in the range (0, spectral radius of A). Works for binary, weighted, and directed graphs. See [1] for more details.

Note from Gorka Zamora-lopez on directed graphs: For the adjacency matrix ‘A’, A_{ij} = 1 indicates j –> i, which is the opposite of the conventional graph theory notation. If your adjacency matrices follow the graph theory convention, ensure to transpose it first.

Args:

adjacency_matrix (np.ndarray): A square and nonnegative matrix representing network’s structure. alpha (float, optional): The scaling factor. Defaults to 1, meaning that no scaling is applied. normalize (bool, optional): If True, applies strength normalization to the matrix [2]. Defaults to False.

Returns:

np.ndarray: The (scaled) communicability matrix. Shape: (N, N)

References:

[1] https://arxiv.org/abs/2307.02449

[2] https://royalsocietypublishing.org/doi/full/10.1098/rsif.2008.0484

yanat.core.default_game(complements: tuple, adjacency_matrix: ~numpy.ndarray | str, index: int, input_noise: ~numpy.ndarray, model: callable = CPUDispatcher(<function simulate_dynamical_system>), model_params: dict | None = None) ndarray[source]

Lesions the given nodes and simulates the dynamics of the system afterwards. Lesioning here means setting the incoming and outgoing connections of the node to zero

Args: complements (tuple): Indices of nodes to be lesioned. Comes from MSA, don’t worry about it suger. adjacency_matrix (Union[np.ndarray, str]): The adjacency matrix representing the system or a path to a pickle file containing the adjacency matrix. The pickling thing makes it faster so I recommend that. index (int): Index of the target node whose activity is to be returned. Also comes from MSA. input_noise (np.ndarray): Input noise/signal for the dynamical model. Shape (N,T). model (callable, optional): The dynamical system model function to simulate with. Defaults to a linear dynamical system. model_params (dict, optional): Additional keyword arguments to pass to the model function.

Returns:

np.ndarray: Resulted activity of the target node given the lesion. Shape is (T,)

yanat.core.identity(x: float | ndarray) float | ndarray[source]
The identity function. It’s for the linear case and I literally stole it from Fabrizio:

https://github.com/fabridamicelli/echoes/blob/master/echoes/utils.py

Args:

x (Union[float, np.ndarray]): input. can be a float or an np array.

Returns:

Union[float, np.ndarray]: output. will be whatever the input is!

yanat.core.lam(adjacency_matrix: ndarray, alpha: float = 0.5, normalize: bool = False) ndarray[source]

Computes the influence matrix for the Linear Attenuation Model (LAM), which underpins the dynamics of Katz centrality and is similar to communicability, but with a linear discount on longer walks rather than an exponential one. The discount factor ‘alpha’ should be less than the spectral radius of the adjacency matrix. See [1] for more details.

Note from Gorka Zamora-lopez on directed graphs: For the adjacency matrix ‘A’, A_{ij} = 1 indicates j –> i, which is the opposite of the conventional graph theory notation. If your adjacency matrices follow the graph theory convention, ensure to transpose it first.

Args:

adjacency_matrix (np.ndarray): A square and nonnegative matrix representing network’s structure. alpha (float): the decay rate. The smaller the decay rate, the quicker it assumes the walks to be subsiding. normalize (bool, optional): If True, applies strength normalization [2] to the matrix. Defaults to False.

Returns:

np.ndarray: The influence matrix for LAM. Shape: (N, N)

References:

[1] https://arxiv.org/abs/2307.02449

[2] https://royalsocietypublishing.org/doi/full/10.1098/rsif.2008.0484

yanat.core.optimal_influence(n_elements: int, game: callable = <function default_game>, game_kwargs: dict | None = None, msa_kwargs: dict | None = None) ShapleyModeND[source]

Estimates the optimal influence of each node in a given network using the MSA algorithm. Note that this function might take considerable time, like even days or weeks to run, depending on the size of the network and computational power of your system. My personal recommendation is to try with fewer than 200 nodes on normal desktop computers but go on a server if there are more nodes. On my own computer with 16 threads, it took about 2h to run a network of N=150 nodes. Also, you don’t need to run your simulations for long, even 1 second is enough. However, if you have delayed systems then make sure the delay is less than 1 second otherwise the source technically doesn’t have time to influence the target.

Args:

n_elements (int): The number of elements in the game (nodes in the network). game (callable, optional): The game function to be used for estimating the optimal influence. Defaults to default_game. game_kwargs (dict, optional): Additional keyword arguments to pass to the game function. msa_kwargs (dict, optional): Additional keyword arguments to pass to the MSA.

Returns:

ShapleyModeND: The estimated optimal influence of each node over the others at each time point. This is basically a multi-index pandas dataframe.

yanat.core.relu(x: float | int | ndarray) float | ndarray[source]

Computes the relu of the input:

Args:

x(Union[float, int, np.ndarray]): input. can be a float or an np array.

Returns:

Union[float, np.ndarray]: output, squashed between 0 and 1.

yanat.core.sar(adjacency_matrix: ndarray, alpha: float = 0.5, normalize: bool = False) ndarray[source]

Computes the analytical covariance matrix for the spatial autoregressive (SAR) model.

The SAR model considers each region’s activity as a weighted sum of fluctuations from other regions, adjusted by a spatial influence factor ‘alpha’, plus a unit-variance Gaussian noise. The covariance matrix is analytically derived as the inverse of (I - alpha * A) times its transpose. See [1].

Args:

adjacency_matrix (np.ndarray): A square, nonnegative, and symmetric matrix representing network’s structure. alpha (float): The spatial influence factor, should be less than the spectral radius of ‘adjacency_matrix’. The smaller the decay rate, the quicker it assumes the walks to be subsiding. normalize (bool, optional): If True, normalizes the matrix using strength normalization described by [2]. Defaults to False.

Returns:

np.ndarray: The covariance matrix of the SAR model. Shape: (N, N)

References:

[1] https://journals.plos.org/ploscompbiol/article?id=10.1371/journal.pcbi.1003530

[2] https://royalsocietypublishing.org/doi/full/10.1098/rsif.2008.0484

yanat.core.simulate_dynamical_system(adjacency_matrix: ~numpy.ndarray, input_matrix: ~numpy.ndarray, coupling: float = 1, dt: float = 0.001, duration: int = 10, timeconstant: float = 0.01, function: callable = CPUDispatcher(<function identity>)) ndarray[source]

Simulates a dynamical system described by the given paramteres.

Args:

adjacency_matrix (np.ndarray): The adjacency matrix (N,N) make sure the matrix is normalized so the system remain stable. we can use ut.spectral_normalization(1,A) to normalize the matrix A. input_matrix (np.ndarray): Input of shape (N, T) where N is the number of nodes and T is the number of time steps. coupling (float, optional): The coupling strength between each node (scales the adjacency_matrix). Defaults to 1. This is another thing to take care of. If the system is unstable, try to reduce the coupling, like to 0.9. dt (float, optional): The time step of the simulation. Defaults to 0.001. duration (int, optional): The duration of the simulation in seconds. Defaults to 10. timeconstant (float, optional): The time constant of the nodes, I think it’s the same as the ‘relaxation time’. Defaults to 0.01. function (Callable, optional): The activation function. Defaults to identity, which means it’s linear.

Returns:

np.ndarray: The state of the dynamical system at each time step so again, the shape is (N, T)

yanat.core.tanh(x: float | int | ndarray) float | ndarray[source]

Computes the hyperbolic tangent of the input. Again, I stole this from Fabrizio: https://github.com/fabridamicelli/echoes/blob/master/echoes/utils.py

Args:

x (Union[float, int, np.ndarray]): input. can be a float or an np array.

Returns:

Union[float, np.ndarray]: output, squashed between -1 and 1.

yanat.utils

yanat.utils.find_density(adjacency_matrix: ndarray) float[source]

Finds the density of the given adjacency matrix. It’s the ratio of the number of edges to the number of possible edges.

Args:

adjacency_matrix (np.ndarray): The adjacency matrix of the network.

Returns:

float: The density of the network.

yanat.utils.log_minmax_normalize(adjacency_matrix: ndarray) ndarray[source]

It first takes the logarithm of the data and then normalizes it between 0 and 1. It also takes care of the infinit values and those nasty things.

Args:

adjacency_matrix (np.ndarray): Adjacency matrix of the network. Technically can be any matrix but I did it for the adjacency matrices.

Returns:

np.ndarray: Normalized data with the same shape as the input.

yanat.utils.log_normalize(adjacency_matrix: ndarray) ndarray[source]

Returns the logarithm of the data (adjacency_matrix) but also takes care of the infinit values.

Args:

adjacency_matrix (np.ndarray): Adjacency matrix of the network. Technically can be any matrix but I did it for the adjacency matrices.

Returns:

np.ndarray: Normalized data with the same shape as the input.

yanat.utils.minmax_normalize(data: DataFrame | ndarray) DataFrame | ndarray[source]

Normalizes data between 0 and 1.

Args:

data (Union[pd.DataFrame, np.ndarray]): Data to be normalized. Can be a DataFrame or an np array but in both cases it should be at most 2D.

Returns:

Union[pd.DataFrame, np.ndarray]: Normalized data with the same shape as the input.

yanat.utils.optimal_influence_default_values(adjacency_matrix: ndarray, location: str = 'adjacency_matrices_for_oi', random_seed: int = 11) dict[source]

Returns the default values for the parameters of the optimal_influence function.

Parameters:

adjacency_matrix (np.ndarray): The adjacency matrix representing the network structure. location (str, optional): The location to save the adjacency matrix file. Defaults to “adjacency_matrices_for_oi”. random_seed (int, optional): The random seed for generating input noise. Defaults to 11.

Returns:

dict: Default values for the parameters of the optimal_influence function.

yanat.utils.simple_fit(parameter_space: list[ParameterGrid], target_matrix: ndarray, model: callable, model_kwargs: dict | None = None, normalize: bool | callable = False) ParameterGrid[source]

Simple (and honestly, ugly) fitting function to find the best parameters for a (communication) model. Does a normal for-loop so it’s not as efficient but at the moment, doesn’t need to be either!

Args:

parameter_space (list): Parameter space to search in, as of now, it should be a list[ParameterGrid] and I gotta fix it! target_matrix (np.ndarray): Which matrix to compare the model’s output to, usually FC or OI. model (callable): Which (communication) model to use. model_kwargs (Optional[dict], optional): Extra things that the model wants. Defaults to None. normalize (Union[bool, callable], optional): If the output needs to be normalized before taking correlation. Defaults to False.

Returns:

list: Updated copy of the parameter space with the correlation values.

yanat.utils.spectral_normalization(target_radius: float, adjacency_matrix: ndarray) ndarray[source]

Normalizes the adjacency matrix to have a spectral radius of the target_radius. Good to keep the system stable.

Args:

target_radius (float): A value below 1.0. It’s the spectral radius that you want to achieve. But use 1.0 if you’re planning to change the global coupling strength somewhere. adjacency_matrix (np.ndarray): Adjacency matrix of the network.

Returns:

np.ndarray: Normalized adjacency matrix with the same shape as the input.

yanat.utils.strength_normalization(adjacency_matrix: ndarray) ndarray[source]

Normalizes the adjacency matrix to subside the effect of high-strength (or high-degree) nodes. This function implements the strength normalization algorithm described in [1]. The algorithm aims to reduce the influence of high-strength nodes in a network by scaling the adjacency matrix.

Parameters:

adjacency_matrix (np.ndarray): The adjacency matrix of the network. It should be a square matrix of shape (n, n), where n is the number of nodes in the network.

Returns:

np.ndarray: The normalized adjacency matrix with the same shape as the input.

References:

[1] https://royalsocietypublishing.org/doi/full/10.1098/rsif.2008.0484