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.

Parameters:
  • 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:

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

Return type:

np.ndarray

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:

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

Return type:

np.ndarray

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

Parameters:

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

Returns:

output. will be whatever the input is!

Return type:

Union[float, np.ndarray]

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.

Parameters:
  • 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:

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

Return type:

np.ndarray

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.

Parameters:
  • 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:

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

Return type:

ShapleyModeND

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

Computes the relu of the input:

Parameters:

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

Returns:

output, squashed between 0 and 1.

Return type:

Union[float, np.ndarray]

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.

Parameters:
  • 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 (try to reduce the) – The coupling strength between each node (scales the adjacency_matrix). Defaults to 1.

  • unstable (This is another thing to take care of. If the system is)

  • coupling

  • 0.9. (like to)

  • 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:

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

Return type:

np.ndarray

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

Parameters:

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

Returns:

output, squashed between -1 and 1.

Return type:

Union[float, np.ndarray]

yanat.utils

yanat.utils.calculate_endpoint_similarity(synthetic_matrix, empirical_matrix)[source]
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.

Parameters:

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

Returns:

The density of the network.

Return type:

float

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.

Parameters:

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

Returns:

Normalized data with the same shape as the input.

Return type:

np.ndarray

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.

Parameters:

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

Returns:

Normalized data with the same shape as the input.

Return type:

np.ndarray

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

Normalizes data between 0 and 1.

Parameters:

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:

Normalized data with the same shape as the input.

Return type:

Union[pd.DataFrame, np.ndarray]

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:

Default values for the parameters of the optimal_influence function.

Return type:

dict

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!

Parameters:
  • 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:

Updated copy of the parameter space with the correlation values.

Return type:

list

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.

Parameters:
  • 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:

Normalized adjacency matrix with the same shape as the input.

Return type:

np.ndarray

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:

The normalized adjacency matrix with the same shape as the input.

Return type:

np.ndarray

References

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

yanat.generative_game_theoric

yanat.generative_game_theoric.compute_component_sizes(adjacency: ndarray[tuple[Any, ...], dtype[float64]]) ndarray[tuple[Any, ...], dtype[float64]][source]

Computes the size of the connected component for each node using BFS.

This function uses Numba for performance. It iterates over all nodes, and for each unvisited node, it performs a Breadth-First Search (BFS) to find all reachable nodes, counting them to determine the component size.

Parameters:

adjacency – The adjacency matrix (n_nodes, n_nodes).

Returns:

An array of size (n_nodes,) where the i-th element is the size of the connected component containing node i.

yanat.generative_game_theoric.compute_local_payoff_impact(distance_matrix: ndarray[tuple[Any, ...], dtype[float64]], adjacency_matrix: ndarray[tuple[Any, ...], dtype[float64]], distance_fn: Callable[[ndarray[tuple[Any, ...], dtype[float64]], ndarray[tuple[Any, ...], dtype[float64]]], ndarray[tuple[Any, ...], dtype[float64]]], alpha: float, beta: float = 1.0, distance_fn_kwargs: Dict[str, Any] | None = None, n_jobs: int = -1) ndarray[tuple[Any, ...], dtype[float64]][source]

Computes the impact on payoff for connected nodes when their edge is removed.

This function iterates through all existing edges, temporarily removes each one, and calculates the change in payoff for the two connected nodes.

Parameters:
  • distance_matrix – Pre-computed distance matrix (n_nodes, n_nodes).

  • adjacency_matrix – The binary, symmetric adjacency matrix (n_nodes, n_nodes).

  • distance_fn – Function to compute the ‘communication’ distance metric.

  • alpha – Weight of the distance term.

  • beta – Weight of the wiring cost term.

  • distance_fn_kwargs – Additional kwargs for distance_fn.

  • n_jobs – Number of parallel jobs (-1 for all cores).

Returns:

An N x N matrix where matrix[i, j] contains the change in payoff for node i when the edge (i, j) is removed. The matrix is sparse (zeros where no edge exists).

yanat.generative_game_theoric.compute_node_payoff(node: int, adjacency: ndarray[tuple[Any, ...], dtype[float64]], distance_matrix: ndarray[tuple[Any, ...], dtype[float64]], distance_fn: Callable[[...], ndarray[tuple[Any, ...], dtype[float64]]], alpha: float, beta: float, connectivity_penalty: float, node_resources: ndarray[tuple[Any, ...], dtype[float64]] | None = None, distance_fn_kwargs: Dict[str, Any] | None = None) float[source]

Computes the payoff for a single node based on distance, wiring cost, and connectivity.

The payoff is defined as: Payoff = - (alpha * distance_term + beta * wiring_cost + connectivity_penalty * disconnected_nodes)

Parameters:
  • node – Index of the node.

  • adjacency – The current adjacency matrix (n_nodes, n_nodes).

  • distance_matrix – Pre-computed Euclidean distance matrix (n_nodes, n_nodes) for wiring cost.

  • distance_fn – Function to compute the ‘communication’ distance metric (e.g., shortest path).

  • alpha – Weight of the distance term (communication efficiency).

  • beta – Weight of the wiring cost term (physical distance).

  • connectivity_penalty – Penalty for each node not in the same connected component.

  • node_resources – Optional vector of node resources to subsidize wiring cost.

  • distance_fn_kwargs – Additional keyword arguments for distance_fn.

Returns:

The calculated payoff value (typically negative).

yanat.generative_game_theoric.find_optimal_alpha(distance_matrix: ndarray, empirical_connectivity: ndarray, distance_fn: Callable, n_iterations: int = 10000, beta: float = 1.0, alpha_range: tuple[float, float] = (1.0, 100.0), tolerance: float = 0.01, max_search_iterations: int = 20, random_seed: int = 11, batch_size: int = 16, symmetric: bool = True, n_jobs: int = -1, connectivity_penalty: float = 0.0, payoff_tolerance: float = 0.0, **kwargs) Dict[str, Any][source]

Finds the optimal alpha value that produces a network with density closest to empirical.

This function uses a bisection search (with linear interpolation) to find the alpha parameter that results in a generated network with the same density as the provided empirical network.

Parameters:
  • distance_matrix – Precomputed distance matrix (n_nodes, n_nodes).

  • empirical_connectivity – Target connectivity matrix to match density with.

  • distance_fn – Distance metric function.

  • n_iterations – Number of iterations for each simulation.

  • beta – Wiring cost parameter.

  • alpha_range – Range for alpha search (min, max).

  • tolerance – Acceptable difference between densities.

  • max_search_iterations – Maximum number of search iterations.

  • random_seed – Random seed for reproducibility.

  • batch_size – Batch size for parallel processing.

  • symmetric – If True, enforces symmetry in generated networks.

  • n_jobs – Number of parallel jobs.

  • connectivity_penalty – Penalty for connectivity.

  • payoff_tolerance – Threshold for accepting new configuration.

  • **kwargs – Additional arguments passed to simulate_network_evolution.

Returns:

  • ‘alpha’: Optimal alpha value.

  • ’density’: Density of the resulting network.

  • ’evolution’: Full history of adjacency matrices.

Return type:

Dictionary containing

Example

>>> dist_mat = np.random.rand(10, 10)
>>> emp_conn = np.random.randint(0, 2, (10, 10))
>>> result = find_optimal_alpha(
...     distance_matrix=dist_mat,
...     empirical_connectivity=emp_conn,
...     distance_fn=shortest_path_distance
... )
>>> print(f"Optimal alpha: {result['alpha']}")
yanat.generative_game_theoric.get_param_value(param: float | ndarray[tuple[Any, ...], dtype[float64]], t: int) float | ndarray[tuple[Any, ...], dtype[float64]][source]

Retrieves the value of a parameter at a specific time step t.

If the parameter is a scalar, it returns the scalar. If it is a 1D array (trajectory), it returns the value at index t. If it is a 2D array (n_iterations, n_nodes), it returns the row at index t.

Parameters:
  • param – The parameter, either a float or a numpy array.

  • t – The current time step (index).

Returns:

The parameter value at time t.

yanat.generative_game_theoric.heat_kernel_distance(adjacency_matrix: ndarray[tuple[Any, ...], dtype[float64]], t: float = 0.5, eps: float = 1e-10, **kwargs) ndarray[tuple[Any, ...], dtype[float64]][source]

Computes the heat kernel distance matrix at diffusion time t.

The heat kernel distance is defined as -log(exp(-t * L)), where L is the Laplacian.

Parameters:
  • adjacency_matrix – The adjacency matrix (n_nodes, n_nodes).

  • t – Diffusion time parameter.

  • eps – Small constant to avoid log(0).

  • **kwargs – Additional arguments passed to _apply_weighting.

Returns:

The heat kernel distance matrix (n_nodes, n_nodes).

yanat.generative_game_theoric.propagation_distance(adjacency_matrix: ndarray[tuple[Any, ...], dtype[float64]], spatial_decay: float = 0.8, symmetric: bool = True, **kwargs) ndarray[tuple[Any, ...], dtype[float64]][source]

Computes the propagation distance matrix.

The propagation distance is defined as the negative log of the influence matrix. The influence matrix is computed using either the LAM (Linear Attenuation Model) or SAR (Spatial Autoregressive) model.

Parameters:
  • adjacency_matrix – The adjacency matrix (n_nodes, n_nodes).

  • spatial_decay – Decay parameter (0 < spatial_decay < 1/spectral_radius).

  • symmetric – If True, uses SAR model (symmetric influence). If False, uses LAM model (directed influence).

  • **kwargs – Additional arguments passed to _apply_weighting.

Returns:

The propagation distance matrix (n_nodes, n_nodes).

yanat.generative_game_theoric.resistance_distance(adjacency: ndarray[tuple[Any, ...], dtype[float64]], **kwargs) ndarray[tuple[Any, ...], dtype[float64]][source]

Computes resistance distances between all pairs of nodes.

The resistance distance is computed using the Moore-Penrose pseudoinverse of the Laplacian matrix. It treats the graph as an electrical network where edges are resistors.

Parameters:
  • adjacency – The adjacency matrix (n_nodes, n_nodes).

  • **kwargs – Additional arguments passed to _apply_weighting.

Returns:

The resistance distance matrix (n_nodes, n_nodes).

yanat.generative_game_theoric.shortest_path_distance(adjacency_matrix: ndarray[tuple[Any, ...], dtype[float64]], **kwargs) ndarray[tuple[Any, ...], dtype[float64]][source]

Computes shortest-path distances between all pairs of nodes.

Uses Dijkstra’s algorithm via scipy.sparse.csgraph.shortest_path. If the graph is weighted (via weight_coefficient), edge weights are treated as costs (inverted if they represent strength).

Parameters:
  • adjacency_matrix – The adjacency matrix (n_nodes, n_nodes).

  • **kwargs – Additional arguments passed to _apply_weighting.

Returns:

The shortest path distance matrix (n_nodes, n_nodes).

yanat.generative_game_theoric.simulate_network_evolution(distance_matrix: ndarray[tuple[Any, ...], dtype[float64]], n_iterations: int, distance_fn: Callable[[ndarray[tuple[Any, ...], dtype[float64]], ndarray[tuple[Any, ...], dtype[float64]]], ndarray[tuple[Any, ...], dtype[float64]]], alpha: float | ndarray[tuple[Any, ...], dtype[float64]], beta: float | ndarray[tuple[Any, ...], dtype[float64]], connectivity_penalty: float | ndarray[tuple[Any, ...], dtype[float64]], initial_adjacency: ndarray[tuple[Any, ...], dtype[float64]] | None = None, n_jobs: int = -1, batch_size: float | ndarray[tuple[Any, ...], dtype[float64]] = 32, node_resources: ndarray[tuple[Any, ...], dtype[float64]] | None = None, payoff_tolerance: float | ndarray[tuple[Any, ...], dtype[float64]] = 0.0, random_seed: int | None = None, symmetric: bool | None = True, **kwargs) ndarray[tuple[Any, ...], dtype[float64]][source]

Simulates the evolution of a network through game-theoretic payoff optimization.

At each step, random edges are selected and “flipped” (added or removed). The change is accepted if it improves the payoff for at least one of the nodes involved (unilateral consent), subject to a tolerance threshold.

Parameters:
  • distance_matrix – Pre-computed Euclidean distance matrix (n_nodes, n_nodes).

  • n_iterations – Total number of simulation steps.

  • distance_fn – Function to compute the ‘nonphysical’ distance metric, e.g., communication.

  • alpha – Weight of the distance term (float or trajectory array).

  • beta – Weight of the wiring cost term (float or trajectory array).

  • connectivity_penalty – Penalty for disconnected components (float or trajectory array).

  • initial_adjacency – Starting adjacency matrix. If None, starts with a ring lattice.

  • n_jobs – Number of parallel jobs for payoff computation (-1 for all cores).

  • batch_size – Number of potential edge flips to evaluate per iteration.

  • node_resources – Optional resources for each node to subsidize wiring costs.

  • payoff_tolerance – Minimum payoff improvement required to accept a change.

  • random_seed – Seed for random number generator.

  • symmetric – If True, enforces undirected edges (symmetry).

  • **kwargs – Additional arguments passed to distance_fn.

Returns:

A 3D array of shape (n_nodes, n_nodes, n_iterations) containing the adjacency matrix at each time step.

Example

>>> dist_mat = np.random.rand(10, 10)
>>> history = simulate_network_evolution(
...     distance_matrix=dist_mat,
...     n_iterations=100,
...     distance_fn=shortest_path_distance,
...     alpha=1.0,
...     beta=0.5,
...     connectivity_penalty=10.0
... )
yanat.generative_game_theoric.topological_distance(adj_matrix: ndarray[tuple[Any, ...], dtype[float64]], **kwargs) ndarray[tuple[Any, ...], dtype[float64]][source]

Computes pairwise topological distance based on cosine similarity of neighbors.

Returns 1 - cosine_similarity. Nodes with similar connectivity patterns will have small topological distance.

Parameters:
  • adj_matrix – The binary adjacency matrix (n_nodes, n_nodes).

  • **kwargs – Additional arguments passed to _apply_weighting.

Returns:

The topological distance matrix (n_nodes, n_nodes).

yanat.generative_game_theoric.validate_parameters(sim_length: int, *trajectories: float | ndarray[tuple[Any, ...], dtype[float64]], names: tuple[str, ...], allow_float: tuple[bool, ...], allow_zero: tuple[bool, ...], allow_none: tuple[bool, ...] | None = None, n_nodes: int | None = None, allow_2d: tuple[bool, ...] | None = None) None[source]

Validates simulation parameters to ensure they are of correct type and shape.

This function checks if parameters are either scalars (floats/ints) or arrays matching the simulation length. It also checks for allowed values (zero, None).

Parameters:
  • sim_length – The total number of iterations in the simulation.

  • *trajectories – Variable number of parameter trajectories to validate.

  • names – A tuple of names corresponding to the trajectories, used for error messages.

  • allow_float – A tuple of booleans indicating if a parameter can be a scalar float.

  • allow_zero – A tuple of booleans indicating if a parameter can be zero.

  • allow_none – A tuple of booleans indicating if a parameter can be None.

  • n_nodes – The number of nodes in the network (required for 2D validation).

  • allow_2d – A tuple of booleans indicating if a parameter can be a 2D array (sim_length, n_nodes).

Raises:

ValueError – If any parameter does not meet the specified criteria (type, shape, value).

Example

>>> validate_parameters(
...     1000,
...     1.0, np.zeros(1000),
...     names=('alpha', 'beta'),
...     allow_float=(True, False),
...     allow_zero=(False, True)
... )

yanat.generative_game_theoric_numba

yanat.generative_game_theoric_numba.compute_node_payoff(node: int, adjacency: ndarray, distance_matrix: ndarray, distance_fn_type: int, alpha: float, beta: float, connectivity_penalty: float, node_resources: ndarray | None, spatial_decay: float, symmetric: bool, weight_coefficient: float, t_param: float) float[source]

Computes the payoff for a single node based on distance, wiring cost, and connectivity.

The payoff is defined as: Payoff = - (alpha * distance_term + beta * wiring_cost + connectivity_penalty * disconnected_nodes)

Parameters:
  • node – Index of the node.

  • adjacency – The current adjacency matrix (n_nodes, n_nodes).

  • distance_matrix – Pre-computed Euclidean distance matrix (n_nodes, n_nodes).

  • distance_fn_type – Integer code for distance function (0: prop, 1: res, 2: heat, 3: sp, 4: topo).

  • alpha – Weight of the distance term.

  • beta – Weight of the wiring cost term.

  • connectivity_penalty – Penalty for disconnected components.

  • node_resources – Optional vector of node resources.

  • spatial_decay – Parameter for propagation distance.

  • symmetric – Parameter for propagation distance.

  • weight_coefficient – Parameter for distance weighting.

  • t_param – Parameter for heat kernel distance.

Returns:

The calculated payoff value.

yanat.generative_game_theoric_numba.find_optimal_alpha(distance_matrix: ndarray, empirical_connectivity: ndarray, distance_fn: Callable, n_iterations: int = 10000, beta: float = 1.0, alpha_range: tuple[float, float] = (1.0, 100.0), tolerance: float = 0.01, max_search_iterations: int = 20, random_seed: int = 11, batch_size: int = 16, symmetric: bool = True, n_jobs: int = -1, connectivity_penalty: float = 0.0, payoff_tolerance: float = 0.0, verbose: bool = True, **kwargs) Dict[str, Any][source]

Finds the optimal alpha value that produces a network with density closest to empirical.

This function uses a bisection search (with linear interpolation) to find the alpha parameter that results in a generated network with the same density as the provided empirical network.

Parameters:
  • distance_matrix – Precomputed distance matrix (n_nodes, n_nodes).

  • empirical_connectivity – Target connectivity matrix to match density with.

  • distance_fn – Distance metric function.

  • n_iterations – Number of iterations for each simulation.

  • beta – Wiring cost parameter.

  • alpha_range – Range for alpha search (min, max).

  • tolerance – Acceptable difference between densities.

  • max_search_iterations – Maximum number of search iterations.

  • random_seed – Random seed for reproducibility.

  • batch_size – Batch size for parallel processing.

  • symmetric – If True, enforces symmetry in generated networks.

  • n_jobs – Number of parallel jobs.

  • connectivity_penalty – Penalty for connectivity.

  • payoff_tolerance – Threshold for accepting new configuration.

  • verbose – If True, prints search progress.

  • **kwargs – Additional arguments passed to simulate_network_evolution.

Returns:

  • ‘alpha’: Optimal alpha value.

  • ’density’: Density of the resulting network.

  • ’evolution’: Full history of adjacency matrices.

Return type:

Dictionary containing

Example

>>> dist_mat = np.random.rand(10, 10)
>>> emp_conn = np.random.randint(0, 2, (10, 10))
>>> result = find_optimal_alpha(
...     distance_matrix=dist_mat,
...     empirical_connectivity=emp_conn,
...     distance_fn=shortest_path_distance
... )
>>> print(f"Optimal alpha: {result['alpha']}")
yanat.generative_game_theoric_numba.heat_kernel_distance(adjacency_matrix: ndarray, t: float = 0.5, eps: float = 1e-10, distance_matrix: ndarray | None = None, weight_coefficient: float = 0.0) ndarray[source]

Computes the heat kernel distance matrix at diffusion time t using Numba.

The heat kernel distance is defined as -log(exp(-t * L)), where L is the Laplacian.

Parameters:
  • adjacency_matrix – The adjacency matrix (n_nodes, n_nodes).

  • t – Diffusion time parameter.

  • eps – Small constant to avoid log(0).

  • distance_matrix – The distance matrix (n_nodes, n_nodes). Required if weight_coefficient > 0.

  • weight_coefficient – The decay coefficient for distance weighting.

Returns:

The heat kernel distance matrix (n_nodes, n_nodes).

yanat.generative_game_theoric_numba.propagation_distance(adjacency_matrix: ndarray, spatial_decay: float = 0.8, symmetric: bool = True, distance_matrix: ndarray | None = None, weight_coefficient: float = 0.0) ndarray[source]

Computes the propagation distance matrix using Numba.

NOTE: This is a bit unstable under Numba with larger spatial decay values. Try heat kernel distance for symmetric graphs, they’re highly correlated.

The propagation distance is defined as the negative log of the influence matrix. The influence matrix is computed using either the LAM (Linear Attenuation Model) or SAR (Spatial Autoregressive) model.

Parameters:
  • adjacency_matrix – The adjacency matrix (n_nodes, n_nodes).

  • spatial_decay – Decay parameter (0 < spatial_decay < 1/spectral_radius).

  • symmetric – If True, uses SAR model (symmetric influence). If False, uses LAM model (directed influence).

  • distance_matrix – The distance matrix (n_nodes, n_nodes). Required if weight_coefficient > 0.

  • weight_coefficient – The decay coefficient for distance weighting.

Returns:

The propagation distance matrix (n_nodes, n_nodes).

yanat.generative_game_theoric_numba.resistance_distance(adjacency: ndarray, distance_matrix: ndarray | None = None, weight_coefficient: float = 0.0) ndarray[source]

Computes resistance distances between all pairs of nodes using Numba.

The resistance distance is computed using the Moore-Penrose pseudoinverse of the Laplacian matrix. It treats the graph as an electrical network where edges are resistors.

Parameters:
  • adjacency – The adjacency matrix (n_nodes, n_nodes).

  • distance_matrix – The distance matrix (n_nodes, n_nodes). Required if weight_coefficient > 0.

  • weight_coefficient – The decay coefficient for distance weighting.

Returns:

The resistance distance matrix (n_nodes, n_nodes).

yanat.generative_game_theoric_numba.shortest_path_distance(adjacency_matrix: ndarray, distance_matrix: ndarray | None = None, weight_coefficient: float = 0.0) ndarray[source]

Computes shortest-path distances between all pairs of nodes using Floyd-Warshall (Numba).

Parameters:
  • adjacency_matrix – The adjacency matrix (n_nodes, n_nodes).

  • distance_matrix – The distance matrix (n_nodes, n_nodes). Required if weight_coefficient > 0.

  • weight_coefficient – The decay coefficient for distance weighting.

Returns:

The shortest path distance matrix (n_nodes, n_nodes).

yanat.generative_game_theoric_numba.simulate_network_evolution(distance_matrix: ndarray, n_iterations: int, distance_fn: str | Callable = 'propagation', alpha: float | ndarray = 1.0, beta: float | ndarray = 1.0, connectivity_penalty: float | ndarray = 0.0, initial_adjacency: ndarray | None = None, n_jobs: int = -1, batch_size: int | ndarray = 32, node_resources: ndarray | None = None, payoff_tolerance: float | ndarray = 0.0, random_seed: int | None = None, symmetric: bool = True, spatial_decay: float = 0.8, weight_coefficient: float = 0.0, t: float = 0.5, verbose: bool = True, **kwargs) ndarray[source]

Simulates the evolution of a network through game-theoretic payoff optimization using Numba.

At each step, random edges are selected and “flipped” (added or removed). The change is accepted if it improves the payoff for at least one of the nodes involved (unilateral consent), subject to a tolerance threshold.

Parameters:
  • distance_matrix – Pre-computed Euclidean distance matrix (n_nodes, n_nodes).

  • n_iterations – Total number of simulation steps.

  • distance_fn – Name of the distance function (“propagation”, “resistance”, “heat”, “shortest”, “topological”) or the function itself.

  • alpha – Weight of the distance term (float or trajectory array).

  • beta – Weight of the wiring cost term (float or trajectory array).

  • connectivity_penalty – Penalty for disconnected components (float or trajectory array).

  • initial_adjacency – Starting adjacency matrix. If None, starts with a ring lattice.

  • n_jobs – Ignored in Numba implementation (uses internal parallelism).

  • batch_size – Number of potential edge flips to evaluate per iteration.

  • node_resources – Optional resources for each node to subsidize wiring costs.

  • payoff_tolerance – Minimum payoff improvement required to accept a change.

  • random_seed – Seed for random number generator.

  • symmetric – If True, enforces undirected edges (symmetry).

  • spatial_decay – Decay parameter for propagation distance.

  • weight_coefficient – Weight coefficient for distance weighting.

  • t – Time parameter for heat kernel distance.

  • verbose – If True, shows progress bar.

  • **kwargs – Additional arguments (ignored).

Returns:

A 3D array of shape (n_nodes, n_nodes, n_iterations) containing the adjacency matrix at each time step.

yanat.generative_game_theoric_numba.topological_distance(adj_matrix: ndarray, distance_matrix: ndarray | None = None, weight_coefficient: float = 0.0) ndarray[source]

Computes pairwise topological distance based on cosine similarity of neighbors using Numba.

Returns 1 - cosine_similarity. Nodes with similar connectivity patterns will have small topological distance.

Parameters:
  • adj_matrix – The adjacency matrix (n_nodes, n_nodes).

  • distance_matrix – The distance matrix (n_nodes, n_nodes). Required if weight_coefficient > 0.

  • weight_coefficient – The decay coefficient for distance weighting.

Returns:

The topological distance matrix (n_nodes, n_nodes).