Skip to content

MaxBromberg/Networks_Via_Diffusion

Repository files navigation

Networks Via Diffusion

This repository contains the working code relevant for Max Bromberg's physics master's thesis, the objective of which is to discover the dependence of network structure on the forms of (presumably information) diffusion.

Supervised by Dr. Philipp Lorenz-Spreen of the Max Planck Institute for Human Development (Max Planck Institut für Bildungsforschung) and Prof. Roland Netz of Freie Universität Berlin.

Hierarchy Coordinates

Independent of the rest of this repository is an implementation of Hierarchy Coordinates for weighted graphs, which may be used in isolation. Originally formulated exclusively for unweighted directed networks0.1 by Corominas-Murta et al. (2013) in On the Origins of Hierarchy in Complex Networks, and subsequently implemented in Matlab v7, hierarchy_coordinates.py provides a python3 implementation which includes weighted networks. An illustration of linear vs exponential threshold distributions (right) and the corresponding unweighted networks created from them (left). For details regarding the adaptation from unweighted to weighted networks, see the associated thesis Calculation of hierarchy coordinates for weighted networks are performed simply by reducing weighted networks to a set of unweighted networks based on either exponentially or linearly distributed thresholds, and subsequently averaging their hierarchy coordinates as given by the original algorithm described in their appendix.

There are a variety of functions required for the calculation of hierarchy coordinates, including initially reducing a simple, directed graph into a node weighted Directed Acyclic Graph (DAG), and thereafter making use of: max_min_layers, (recursive)_leaf_removal, graph_entropy, and the base hierarchy coordinate functions0.2. Naturally all may be used independently, or for the common purpose of simply finding the hierarchy coordinates of a given graph (including a binary graph, for which the thresholds are not used) one may simply use hierarchy_coordinates.average_hierarchy_coordinates, as in this example code:

import numpy as np
import hierarchy_coordinates as hc

Adjacency_Matrix = np.random.rand(10, 10)
orderability, feedforwardness, treeness = hc.average_hierarchy_coordinates(Adjacency_Matrix)

Furthermore, for plotting of hierarchy coordinates with 2d planes printed below, one may use the plotter.py general_3d_data_plot function, as below. Hierarchy coordinates for the associated model with power law seeding, and <k> = 0.25 with associated 2d plots below. Color coding here is based on selectivity value.

0.1 Supplementary material of Corominas-Murta et al. (2013) with detailed description of the hierarchy coordinates used to develop this repository's hierarchy_coordinates.py found here

0.2 Feedforwardness and treeness are calculated through recursive application of the same 'base' version of their respective functions feedforwardnes_iteration and single_graph_treeness.

Efficiency Coordinates

Note: Implementation pending further analysis, as normalization does not presently yield expected (0, 1)2 range

This repository also contains an implementation of the efficiency coordinates as explored in Goñi et al. (2013) Exploring the morphospace of communication efficiency in complex networks, and initially developed in Latora et Marchiori (2001), which considers both global, or routing efficiency (Erout) and diffusion efficiency (Ediff). Routing efficiencies consider the relative efficiency of intra-network communication via shortest possible path's (implying global knowledge, parallel processing) whereas diffusion efficiency1.0 considers the comparable efficacy of information passing via pure diffusion.

The efficiency coordinates require only an adjacency (numpy) matrix (which will be row normalized if not already normalized) and the normalization option normalizes the efficiency coordinates according to the method in Goñi et al. (2013).

import efficiency_coordinates as ef
import numpy as np

A = np.random.rand(10, 10)  # Declaring Adjacency Matrix
E_diff, E_rout = ef.network_efficiencies(A, normalize=True)

# Equivalent to:
E_diff, E_rout = ef.E_diff(A), ef.E_rout(A)

1.1 Diffusive efficiency makes use of the random walker effective distance (explored in Iannelli et al. 2017), which is a handy method whereby with maximal computational demands of matrix inversion ~O(n2.5) yields the random walker probabilities between all nodes in a network.

Using the Migrating Meme Model

Though graph.py encompasses the mechanics of the simulation itself, and its internal documentation is intended to clarify the purpose of its component functions, those wishing to simply run simulations with a given model configuration and parameter combination need not interact with the file directly.

Individual Simulations

For singular simulations, it is recommended to familiarize oneself with the possible parameter variations defined in the initialization of graph.py, though as all but number of nodes is given as a default argument, one may readily learn by simply initializing a graph class object with a given number of nodes, selectivity and edge_conservation, initializing its structure, and subsequently simulating and plotting. A most basic example:

import graph
import plotter

edge_conservation = 0.15  # Between (0, 1), determines node's willingness to adapt edges
selectivity = 0.75  # Between (0, 1), determines % edges/node rewarded
n = 10

G = graph.Graph(n, edge_conservation, selectivity)
G.uniform_random_edge_init()
G.simulate(num_runs=4, constant_source_node=2)

plotter.plot_network(G, show=True)

Plot produced via the above code; use plotter.plot_single_network to plot a network at a given timestep. The plotter.py file contains many preformatted, relevant plotting functions for a given network, from effective distance evolution and edge distribution histograms to grid search heatmaps and network evolution animations. Nearly all plotter.py functions accept a graph class object as their first argument, and can either be directly shown via setting show=True or saved via title='/path/to/file/file_title'.

Note that for ensemble simulations (i.e. using G.simulate_ensemble(num_simulations=10, num_runs_per_sim=100, edge_init=1.2)) one needs to use the edge_init parameter2.1, to determine the edge initialization for each simulation. Furthermore, the final adjacency matrix is an average of all simulations, which makes sense for some observables, as in effective distance, but doesn't make much sense for visualizing network graphs.

Grid Searches

If it's grid searches y'er afta', grid_search_control.py allows for all possible model configurations for grid searches over selectivity and edge conservation parameters, as well as a selection of resultant graphs. Adjust directory variable to intended output directory, and then shift model configuration via adjustments to the default configuration dictionary like so:

example_configuration_dict = {**default_dict, edge_init: 1.2}

Where here the example_configuration_dict has shifted the grid search to run with edge_init value of 1.2, instead of the default (See the globally defined default dictionaries parameter_dic, search_wide_dic, edge_init, ensemble_params, and plots to learn what the default configuration parameters are and their effect)

If multiple grid searches are intended, they may be strung together as a list of dictionaries as a master_dict and run sequentially via a loop,

if __name__ == '__main__':
for i in range(len(master_dict)):
    run_grid_search(param_dic=master_dict[i])

or else in parallel via accepting a system arg indexing through the master_dict, as potentially in the case of cluster computing, e.g.

if __name__ == '__main__':
    run_grid_search(param_dic=master_dict[int(sys.argv[1])])

When wishing to vary network initialization, seeding or binaries, one may make use of the list_of_dicts function, which combinatorically combines all alterations listed through other dictionaries and graphs them onto a 'base dictionary' of common features. Thus if the entire space of varying initial network structure, seeding and mechanics is to be explored, then one may simply enter their intended 'base dictionary' as the initial argument to the list_of_dicts function, and then use the subsequent functions initializations_dic, seeding_dic, directionality_dic to generate all combinations of initial structures, seeding and mechanics, respectively, e.g.

master_dict = list_of_dicts(default_dict, initializations_dic(directory), seeding_dic(directory), directionality_dic(directory))

Note that for cluster computing, it may be necessary to turn off the Xwindows backend of matplotlib via uncommenting line 3 of plotter.py, matplotlib.use('Agg'), disabling matplotlib graph displays.

2.1 edge_init determines initial network structure based on data type, None is uniform rnd, int is num_edges/node in sparse init, float is degree exp in scale free init.

About

A model of network diffusion to resolve their resultant adaptation and corresponding structure.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published