|
| 1 | +# Example |
| 2 | + |
| 3 | +Here we are going to synthetically illustrate some of the main features of MultilayerGraphs.jl. For a more comprehensive exploration of the package functionalities we strongly recommend to consult the [documentation](https://juliagraphs.org/MultilayerGraphs.jl). |
| 4 | + |
| 5 | +## Installation |
| 6 | + |
| 7 | +To install MultilayerGraphs.jl it is sufficient to activate the `pkg` mode by pressing `]` in the Julia REPL and then run the following command: |
| 8 | + |
| 9 | +```nothing |
| 10 | +pkg> add MultilayerGraphs |
| 11 | +``` |
| 12 | + |
| 13 | +## Usage |
| 14 | + |
| 15 | +Let's begin by importing the necessary dependencies and setting the relevant constants. |
| 16 | + |
| 17 | +```julia |
| 18 | +# Import necessary dependencies |
| 19 | +using Distributions, Graphs, SimpleValueGraphs |
| 20 | +using MultilayerGraphs |
| 21 | +# Set the number of nodes |
| 22 | +const n_nodes = 100 |
| 23 | +# Create a list of nodes |
| 24 | +const node_list = [Node("node_$i") for i in 1:n_nodes] |
| 25 | +``` |
| 26 | + |
| 27 | +### Layers and Interlayers |
| 28 | + |
| 29 | +We will instantiate layers and interlayers with randomly-selected edges and vertices adopting a variety of techniques. Layers and Interlayers are not immutable, and mostly behave like normal graphs. The user is invited to consult the [API](https://juliagraphs.org/MultilayerGraphs.jl/stable/API/) for further details. |
| 30 | + |
| 31 | +Here we define a layer with an underlying simple directed graph using a graph generator-like (or "configuration model"-like) constructor which allows us to specify both the **indegree** and the **outdegree sequences**. Before instantiating each layer we sample the number of its vertices and, optionally, of its edges. |
| 32 | + |
| 33 | +```julia |
| 34 | +# Create a simple directed layer |
| 35 | +n_vertices = rand(1:100) # Number of vertices |
| 36 | +layer_simple_directed = layer_simpledigraph( # Layer constructor |
| 37 | + :layer_simple_directed, # Layer name |
| 38 | + sample(node_list, n_vertices; replace=false), # Nodes represented in the layer |
| 39 | + Truncated(Normal(5, 5), 0, 20), # Indegree sequence distribution |
| 40 | + Truncated(Normal(5, 5), 0, 20) # Outdegree sequence distribution |
| 41 | +) |
| 42 | +``` |
| 43 | + |
| 44 | +Then we define a layer with an underlying simple weighted directed graph. This is another kind of constructor that allows the user to specify the number of edges to be randomly distributed among vertices. |
| 45 | + |
| 46 | +```julia |
| 47 | +# Create a simple directed weighted layer |
| 48 | +n_vertices = rand(1:n_nodes) # Number of vertices |
| 49 | +n_edges = rand(n_vertices:(n_vertices * (n_vertices - 1) - 1)) # Number of edges |
| 50 | +layer_simple_directed_weighted = layer_simpleweighteddigraph( # Layer constructor |
| 51 | + :layer_simple_directed_weighted, # Layer name |
| 52 | + sample(node_list, n_vertices; replace=false), # Nodes represented in the layer |
| 53 | + n_edges; # Number of randomly distributed edges |
| 54 | + default_edge_weight=(src, dst) -> rand() # Function assigning weights to edges |
| 55 | +) |
| 56 | +``` |
| 57 | + |
| 58 | +Similar constructors, more flexible at the cost of ease of use, enable a finer tuning. The constructor we use below should be necessary only in rare circumstances, e.g. if the equivalent simplified constructor `layer_simplevaldigraph` is not able to infer the correct return types of `default_vertex_metadata` or `default_edge_metadata`, or to use and underlying graph structure that isn't currently supported. |
| 59 | + |
| 60 | +```julia |
| 61 | +# Create a simple directed value layer |
| 62 | +n_vertices = rand(1:n_nodes) # Number of vertices |
| 63 | +n_edges = rand(n_vertices:(n_vertices * (n_vertices - 1) - 1)) # Number of edges |
| 64 | +default_vertex_metadata = v -> ("vertex_$(v)_metadata") # Vertex metadata |
| 65 | +default_edge_metadata = (s, d) -> (rand(),) # Edge metadata |
| 66 | +layer_simple_directed_value = Layer( # Layer constructor |
| 67 | + :layer_simple_directed_value, # Layer name |
| 68 | + sample(node_list, n_vertices; replace=false), # Nodes represented in the layer |
| 69 | + n_edges, # Number of randomly distributed edges |
| 70 | + ValDiGraph( |
| 71 | + SimpleDiGraph{Int64}(); |
| 72 | + vertexval_types=(String,), |
| 73 | + vertexval_init=default_vertex_metadata, |
| 74 | + edgeval_types=(Float64,), |
| 75 | + edgeval_init=default_edge_metadata, |
| 76 | + ), |
| 77 | + Float64; |
| 78 | + default_vertex_metadata=default_vertex_metadata, # Vertex metadata |
| 79 | + default_edge_metadata=default_edge_metadata # Edge metadata |
| 80 | +) |
| 81 | + |
| 82 | +# Create a list of layers |
| 83 | +layers = [layer_simple_directed, layer_simple_directed_weighted, layer_simple_directed_value] |
| 84 | +``` |
| 85 | + |
| 86 | +There are many more constructors the user is encouraged to explore in the package [documentation](https://juliagraphs.org/MultilayerGraphs.jl). |
| 87 | + |
| 88 | +The interface of interlayers is very similar to that of layers. It is very important to notice that, in order to define a `Multilayer(Di)Graph`, interlayers don't need to be explicitly constructed by the user since they are automatically identified by the `Multilayer(Di)Graph` constructor, but for more complex interlayers the manual instantiation is required. |
| 89 | + |
| 90 | +Here we define an interlayer with an underlying simple directed graph. |
| 91 | + |
| 92 | +```julia |
| 93 | +# Create a simple directed interlayer |
| 94 | +n_vertices_1 = nv(layer_simple_directed) # Number of vertices of layer 1 |
| 95 | +n_vertices_2 = nv(layer_simple_directed_weighted) # Number of vertices of layer 2 |
| 96 | +n_edges = rand(1:(n_vertices_1 * n_vertices_2 - 1)) # Number of interlayer edges |
| 97 | +interlayer_simple_directed = interlayer_simpledigraph( # Interlayer constructor |
| 98 | + layer_simple_directed, # Layer 1 |
| 99 | + layer_simple_directed_weighted, # Layer 2 |
| 100 | + n_edges # Number of edges |
| 101 | +) |
| 102 | +``` |
| 103 | + |
| 104 | +The interlayer exports a more flexible constructor too. |
| 105 | + |
| 106 | +```julia |
| 107 | +# Create a simple directed meta interlayer |
| 108 | +n_vertices_1 = nv(layer_simple_directed_weighted) # Number of vertices of layer 1 |
| 109 | +n_vertices_2 = nv(layer_simple_directed_value) # Number of vertices of layer 2 |
| 110 | +n_edges = rand(1:(n_vertices_1 * n_vertices_2 - 1)) # Number of interlayer edges |
| 111 | +interlayer_simple_directed_meta = interlayer_metadigraph( # Interlayer constructor |
| 112 | + layer_simple_directed_weighted, # Layer 1 |
| 113 | + layer_simple_directed_value, # Layer 2 |
| 114 | + n_edges; # Number of edges |
| 115 | + default_edge_metadata=(src, dst) -> # Edge metadata |
| 116 | + (edge_metadata="metadata_of_edge_from_$(src)_to_$(dst)"), |
| 117 | + transfer_vertex_metadata=true # Boolean deciding layer vertex metadata inheritance |
| 118 | +) |
| 119 | + |
| 120 | +# Create a list of interlayers |
| 121 | +interlayers = [interlayer_simple_directed, interlayer_simple_directed_meta] |
| 122 | +``` |
| 123 | + |
| 124 | +### Multilayer Graphs |
| 125 | + |
| 126 | +Let's construct a directed multilayer graph (`MultilayerDiGraph`). |
| 127 | + |
| 128 | +```julia |
| 129 | +# Create a simple directed multilayer graph |
| 130 | +multilayerdigraph = MultilayerDiGraph( # Constructor |
| 131 | + layers, # The (ordered) collection of layers |
| 132 | + interlayers; # The manually specified interlayers |
| 133 | + # The interlayers that are left unspecified |
| 134 | + # will be automatically inserted according |
| 135 | + # to the keyword argument below |
| 136 | + default_interlayers_structure="multiplex" |
| 137 | + # The automatically specified interlayers will have only diagonal couplings |
| 138 | +) |
| 139 | + |
| 140 | +# Layers and interlayer can be accessed as properties using their names |
| 141 | +multilayerdigraph.layer_simplevaldigraph |
| 142 | +``` |
| 143 | + |
| 144 | +Then we proceed by showing how to add nodes, vertices and edges to a directed multilayer graph. The user may add vertices that do or do not represent nodes which are already present in the multilayer graph. In the latter case, we have to create a node first and then add the vertex representing such node to the multilayer graph. The vertex-level metadata are effectively considered only if the graph underlying the relevant layer or interlayer supports them, otherwise they are discarded. The same holds for edge-level metadata and/or weight. |
| 145 | + |
| 146 | +```julia |
| 147 | +# Create a node |
| 148 | +new_node_1 = Node("new_node_1") |
| 149 | +# Add the node to the multilayer graph |
| 150 | +add_node!(multilayerdigraph, new_node_1) |
| 151 | +# Create a vertex representing the node |
| 152 | +new_vertex_1 = MV( # Constructor (alias for "MultilayerVertex") |
| 153 | + new_node_1, # Node represented by the vertex |
| 154 | + :layer_simplevaldigraph, # Layer containing the vertex |
| 155 | + ("new_metadata") # Vertex metadata |
| 156 | +) |
| 157 | +# Add the vertex |
| 158 | +add_vertex!( |
| 159 | + multilayerdigraph, # MultilayerDiGraph the vertex will be added to |
| 160 | + new_vertex_1 # MultilayerVertex to add |
| 161 | +) |
| 162 | + |
| 163 | +# Create another node in another layer |
| 164 | +new_node_2 = Node("new_node_2") |
| 165 | +# Create another vertex representing the new node |
| 166 | +new_vertex_2 = MV(new_node_2, :layer_simpledigraph) |
| 167 | +# Add the new vertex |
| 168 | +add_vertex!( |
| 169 | + multilayerdigraph, |
| 170 | + new_vertex_2; |
| 171 | + add_node=true # Add the associated node before adding the vertex |
| 172 | +) |
| 173 | +# Create an edge |
| 174 | +new_edge = MultilayerEdge( # Constructor |
| 175 | + new_vertex_1, # Source vertex |
| 176 | + new_vertex_2, # Destination vertex |
| 177 | + ("some_edge_metadata") # Edge metadata |
| 178 | +) |
| 179 | +# Add the edge |
| 180 | +add_edge!( |
| 181 | + multilayerdigraph, # MultilayerDiGraph the edge will be added to |
| 182 | + new_edge # MultilayerVertex to add |
| 183 | +) |
| 184 | +``` |
| 185 | + |
| 186 | +Finally we illustrate how to compute a few multilayer metrics such as the global clustering coefficient, the overlay clustering coefficient, the multilayer eigenvector centrality, and the multilayer modularity as defined in [De Domenico et al. (2013)](https://doi.org/10.1103/physrevx.3.041022). |
| 187 | + |
| 188 | +```julia |
| 189 | +# Compute the global clustering coefficient |
| 190 | +multilayer_global_clustering_coefficient(multilayerdigraph) |
| 191 | +# Compute the overlay clustering coefficient |
| 192 | +overlay_clustering_coefficient(multilayerdigraph) |
| 193 | +# Compute the multilayer eigenvector centrality |
| 194 | +eigenvector_centrality(multilayerdigraph) |
| 195 | +# Compute the multilayer modularity |
| 196 | +modularity( |
| 197 | + multilayerdigraph, |
| 198 | + rand([1, 2, 3, 4], length(nodes(multilayerdigraph)), length(multilayerdigraph.layers)) |
| 199 | +) |
| 200 | +``` |
0 commit comments