Star us on GitHub!
StarMultilayerGraphs.jl
MultilayerGraphs.jl is a Julia package for the construction, manipulation and analysis of multilayer graphs extending Graphs.jl.
Overview
MultilayerGraphs.jl implements the mathematical formulation of multilayer graphs proposed by De Domenico et al. (2013). It mainly revolves around two custom types, MultilayerGraph
and MultilayerDiGraph
, encoding undirected and directed multilayer graphs respectively.
Roughly speaking, a multilayer graph is a collection of graphs, called layers, whose vertices are representations of the same set of nodes endowed with a relational structure provided by the interlayer: the bipartite graph whose vertices are those of any two consecutive layers and whose edges are those between nodes of the two consecutive layers. See below for the distinction between node and vertex.
MultilayerGraph
and MultilayerDiGraph
are fully-fledged Graphs.jl extensions. Both structs are designed so that their layers and interlayers can be of any type (as long as they are Graphs.jl extensions themselves) and they need not be all of the same type. It is anyway required that all layers and interlayers of MultilayerGraph
and MultilayerDiGraph
are respectively undirected and directed. Directedness is checked via the IsDirected
trait defined in Graphs.jl adopting SimpleTraits.jl. Since the layers' and interlayers' graph types don't need to be the same, multilayer graph types are considered weighted graphs by default, and thus are assigned the trait IsWeighted
.
Installation
Press ]
in the Julia REPL and then
pkg> add https://github.com/InPhyT/MultilayerGraphs.jl
Tutorial
Here we illustrate how to define, handle and analyse a MultilayerGraph
(the directed version is completely analogous).
Layers and Interlayers
Let's import some necessary packages
# Import necessary dependencies
using Graphs, SimpleWeightedGraphs, MultilayerGraphs
We define some methods and constants that will prove useful later in the tutorial (you may come back to these later when they get used)
# Set the number of nodes, minimum and maximum number of edges for random graphs
const n_nodes = 5
const min_edges = n_nodes
const max_edges = 10
# Define methods generating random graphs
get_SimpleGraph() = SimpleGraph(n_nodes, rand(min_edges:max_edges)) # Undirected graph
get_SimpleDiGraph() = SimpleDiGraph(n_nodes, rand(min_edges:max_edges)) # Directed graph
# Define variables for random weighted graphs
const simpleweightedgraph_sources = 1:n_nodes
const simpleweightedgraph_destinations = rand(1:n_nodes, n_nodes)
const simpleweightedgraph_weights = rand(n_nodes)
# Define methods generating random weighted graphs
get_SimpleWeightedGraph() = SimpleWeightedGraph(simpleweightedgraph_sources, rand(1:n_nodes, n_nodes), rand(n_nodes)) # Undirected graph
get_SimpleWeightedDiGraph() = SimpleWeightedDiGraph(simpleweightedgraph_sources, rand(1:n_nodes, n_nodes), rand(n_nodes)) # Directed graph
We proceed by constructing a layer (see Layer
)
# Construct a layer
layer = Layer(:layer_1, SimpleGraph(n_nodes, rand(min_edges:max_edges)); U = Float64)
A Layer
has a name (here :layer_1
), an underlying graph (SimpleGraph(n_nodes, rand(min_edges:max_edges))
) and an adjacency matrix eltype
U
. To correctly specify a multilayer graph all layers and interlayers must have the same U
, otherwise the adjacency tensor would be poorly specified. Notice that U
does not need to coincide with the eltype
of the adjacency matrix of the underlying graph.
As far as we know, there is no way to set it explicitly for all Graphs.jl extensions, nor it is required for extensions to implement such feature, so our package converts to U
the eltype
of Layer
s and Interlayer
s adjacency matrices every time they are invoked
adjacency_matrix(layers[1])
5×5 SparseMatrixCSC{Float64, Int64} with 16 stored entries:
⋅ 1.0 ⋅ 1.0 1.0
1.0 ⋅ 1.0 1.0 ⋅
⋅ 1.0 ⋅ 1.0 1.0
1.0 1.0 1.0 ⋅ 1.0
1.0 ⋅ 1.0 1.0 ⋅
We may define more Layer
s for future use
layers = [
Layer(:layer_1, get_SimpleGraph(); U = Float64),
Layer(:layer_2, get_SimpleWeightedGraph(); U = Float64),
Layer(:layer_3, get_SimpleWeightedGraph(); U = Float64),
]
There are other constructors for the Layer
struct you may want to consult via ?Layer
.
We similarly define an interlayer (see Interlayer
)
interlayer = Interlayer(n_nodes, :interlayer_layer_1_layer_2 , :layer_1, :layer_2, SimpleGraph{Int64}, rand(min_edges:max_edges); U = Float64)
Here we used a constructor that returns a random Interlayer
. Its arguments are the number of nodes n_nodes
, the name of the Interlayer
interlayer_layer_1_layer_2
, the name of the Layers
that it connects (:layer_1
and :layer_2
), the underlying graph type SimpleGraph{Int64}
, and the number of edges rand(min_edges:max_edges)
and the adjacency matrix eltype
again need to be specified (although it may be left blank and the constructor will default to eltype(adjacency_matrix(SimpleGraph{Int64}))
).
The adjacency matrix of an Interlayer
is that of a bipartite graph
adjacency_matrix(interlayer)
10×10 SparseMatrixCSC{Float64, Int64} with 18 stored entries:
⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 1.0
⋅ ⋅ ⋅ ⋅ ⋅ 1.0 ⋅ 1.0 1.0 ⋅
⋅ ⋅ ⋅ ⋅ ⋅ 1.0 ⋅ ⋅ ⋅ ⋅
⋅ ⋅ ⋅ ⋅ ⋅ 1.0 1.0 ⋅ ⋅ 1.0
⋅ ⋅ ⋅ ⋅ ⋅ 1.0 ⋅ ⋅ ⋅ ⋅
⋅ 1.0 1.0 1.0 1.0 ⋅ ⋅ ⋅ ⋅ ⋅
⋅ ⋅ ⋅ 1.0 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅
⋅ 1.0 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅
⋅ 1.0 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅
1.0 ⋅ ⋅ 1.0 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅
It is a 4 block matrix where the first n_nodes
rows and columns refer to :layer_1
's vertices, while the last n_nodes
rows and columns refer to :layer_2
's vertices.
We may define more Interlayer
s for future use:
interlayers = [
Interlayer(n_nodes, :interlayer_layer_1_layer_2, :layer_1, :layer_2, SimpleGraph{Int64}, rand(min_edges:max_edges); U = Float64),
Interlayer(n_nodes, :interlayer_layer_1_layer_3, :layer_1, :layer_3, SimpleWeightedGraph{Int64,Float64}, rand(min_edges:max_edges))
]
There are other constructors for the Interlayer
struct you may want to consult via ?Interlayer
.
Layer
s and Interlayer
s and complete extensions of Graphs.jl, so all methods in Graphs.jl should just work. The explicitly extended methods are edges
, eltype
, edgetype
, has_edge
, has_vertex
, inneighbors
, ne
, nv
, outneighbors
, vertices
, is_directed
, add_edge!
, rem_edge!
.
Instantiation and Handling of MultilayerGraph
We can define a MultilayerGraph
by specifying its layers and interlayers:
multilayergraph = MultilayerGraph(layers, interlayers; default_interlayer = "multiplex")
MultilayerGraph{Int64, Float64}([0.0 1.0 … 1.0 1.0; 1.0 0.0 … 0.0 1.0; … ; 1.0 0.0 … 0.0 0.0; 1.0 1.0 … 0.0 0.0;;; 0.0 0.0 … 0.0 0.0; 0.0 1.0 …],...)
It is not important that the interlayers
array does not contain all the interlayers needed to specify the multilayer graph: the unspecified interlayers are automatically generated according to the default_interlayer
argument. Right now only the "multiplex"
value is supported, and will generate interlayers that have edges between pair of vertices of the two layers that represent the same node.
Notice that in the output the signature of MultilayerGraph
has two parametric types, namely MultilayerGraph{Int64, Float64}
. The first is referred to the node type, that just as in every Graphs.jl
extension it is a subtype of Integer
. The second parameter is instead the eltype
of the equivalent of the adjacency matrix for multilayer graphs: the adjacency tensor (see below for more).
You may also specify random MultilayerGraph
random_multilayergraph = MultilayerGraph( 3, # Number of layers
n_nodes, # Number of nodes
min_edges, # Minimum number of edges in each layer/interlayer
max_edges, # Maximum number of edges in each layer/interlayer
[
SimpleGraph{Int64},
SimpleWeightedGraph{Int64, Float64},
] # The set of graph types to random draw from when constructing layers and interlayers
)
Alternatively, one may add layers and interlayers calling the add_layer!
and specify_interlayer!
functions respectively, perhaps starting with an empty multilayer graph created with the constructor:
empty_multilayergraph = MultilayerGraph( n_nodes, # Number of nodes
Int64, # Node type
Float64 # Adjacency tensor's eltype, see below
)
Let's explore some properties of the MultilayerGraph
struct.
Layers
It is an OrderedDict
where the keys are the layers' indexes within the multilayer graph and the values are the actual Layer
s
multilayergraph.layers
OrderedDict{Tuple{Int64, Int64}, Layer{Int64, U, G} where {U<:Real, G<:AbstractGraph{Int64}}} with 3 entries:
(1, 1) => Layer{Int64, Float64, SimpleGraph{Int64}}(:layer_1, SimpleGraph{Int64}(5, [[2, 4], [1, 3, 4], [2], [1, 2, 5], [4]]), MultilayerVertex{Int64}[], Tuple{MultilayerVertex{Int64}, MultilayerVertex{Int64}}[])
(2, 2) => Layer{Int64, Float64, SimpleWeightedGraph{Int64, Float64}}(:layer_2, {5, 4} undirected simple Int64 graph with Float64 weights, MultilayerVertex{Int64}[], Tuple{MultilayerVertex{Int64}, MultilayerVertex{Int64}}[])
(3, 3) => Layer{Int64, Float64, SimpleWeightedGraph{Int64, Float64}}(:layer_3, {5, 4} undirected simple Int64 graph with Float64 weights, MultilayerVertex{Int64}[], Tuple{MultilayerVertex{Int64}, MultilayerVertex{Int64}}[])
Interlayers
It is an OrderedDict
where each key is the pair of indexes of the layers that the corresponding value, i.e. the interlayer, connects within the multilayer graph
multilayergraph.interlayers
OrderedDict{Tuple{Int64, Int64}, Interlayer{Int64, U, G} where {U<:Real, G<:AbstractGraph{Int64}}} with 6 entries:
(2, 1) => Interlayer{Int64, Float64, SimpleGraph{Int64}}(:interlayer_layer_2_layer_1, :layer_2, :layer_1, SimpleGraph{Int64}(9, [[7, 8, 9, 10], [9], [7], [7], [6, 9], [5], [1, 3, 4], [1], [1, 2, 5], [1]]), MultilayerVertex{Int64}[], Tuple{MultilayerVerte…
(1, 2) => Interlayer{Int64, Float64, SimpleGraph{Int64}}(:interlayer_layer_1_layer_2, :layer_1, :layer_2, SimpleGraph{Int64}(9, [[10], [6, 8, 9], [6], [6, 7, 10], [6], [2, 3, 4, 5], [4], [2], [2], [1, 4]]), MultilayerVertex{Int64}[], Tuple{MultilayerVertex{Int64},…
(3, 1) => Interlayer{Int64, Float64, SimpleWeightedGraph{Int64, Float64}}(:interlayer_layer_3_layer_1, :layer_3, :layer_1, {10, 7} undirected simple Int64 graph with Float64 weights, MultilayerVertex{Int64}[], Tuple{MultilayerVertex{Int64}, MultilayerVer…
(1, 3) => Interlayer{Int64, Float64, SimpleWeightedGraph{Int64, Float64}}(:interlayer_layer_1_layer_3, :layer_1, :layer_3, {10, 4} undirected simple Int64 graph with Float64 weights, MultilayerVertex{Int64}[], Tuple{MultilayerVertex{Int64}, MultilayerVertex{Int64}…
(3, 2) => Interlayer{Int64, Float64, SimpleGraph{Int64}}(:interlayer_layer_3_layer_2, :layer_3, :layer_2, SimpleGraph{Int64}(5, [[6], [7], [8], [9], [10], [1], [2], [3], [4], [5]]), MultilayerVertex{Int64}[], Tuple{MultilayerVertex{Int64}, MultilayerVert…
(2, 3) => Interlayer{Int64, Float64, SimpleGraph{Int64}}(:interlayer_layer_2_layer_3, :layer_2, :layer_3, SimpleGraph{Int64}(5, [[6], [7], [8], [9], [10], [1], [2], [3], [4], [5]]), MultilayerVertex{Int64}[], Tuple{MultilayerVertex{Int64}, MultilayerVert…
Note that the (1,2) interlayer (i.e. the interlayer between layer (1,1) and layer (2,2)) is very similar to interlayer (2,1), but not identical: its adjacency matrix rows and columns are reordered. One may get interlayer (2,1) from interlayer (1,2) (i.e. one may get the symmetric interlayer of (1,2)) as follows
symmetric_interlayer = get_symmetric_interlayer(multilayergraph.interlayers[(1,2)])
symmetric_interlayer == multilayergraph.interlayers[(2,1)]
true
You may access individual layers and interlayers with the "dot" notation:
multilayergraph.layer_1
Layer{Int64, Float64, SimpleGraph{Int64}}(:layer_1, SimpleGraph{Int64}(7, [[2, 3, 4, 5], [1, 4], [1, 5], [1, 2, 5], [1, 3, 4]]), MultilayerVertex{Int64}[], Tuple{MultilayerVertex{Int64}, MultilayerVertex{Int64}}[])
Adjacency Tensor
The adjacency tensor is a 4-dimensional array
multilayergraph.adjacency_tensor
5×5×3×3 Array{Float64, 4}:
...
The adjacency tensor is a 4-dimensional array. To understand its indexing, consider the following example
multilayergraph.adjacency_tensor[1,5,2,3]
0.0
This means that there is an edge of zero weight between the vertex representing node 1 in layer 5 and the vertex representing node 2 in layer 3. It is a good time to note the difference between nodes and vertices. In the context of multilayer graphs, the vertices of every layer and interlayer represent the same set of nodes. That is, vertex 1 in layer (1,1) represents the same node as vertex 1 in layer (2,2) and so on. To make this distinction clearer the package implements the MultilayerVertex
type, that represents vertices within the multilayer graph. The implementation of MultilayerVertex
is
struct MultilayerVertex{T <: Integer} <: AbstractMultilayerVertex{T}
node::T # The node the vertex represents
layer::Symbol # The layer the vertex belongs to
end
To get the vertices of a Layer
or an Interlayer
, one may use Graphs.jl APIs
vertices(multilayergraph.layers[(1,1)])
5-element Vector{MultilayerVertex{Int64}}:
MultilayerVertex{Int64}(1, :layer_1)
MultilayerVertex{Int64}(2, :layer_1)
MultilayerVertex{Int64}(3, :layer_1)
MultilayerVertex{Int64}(4, :layer_1)
MultilayerVertex{Int64}(5, :layer_1)
vertices(multilayergraph.interlayers[(1,2)])
10-element Vector{Any}:
MultilayerVertex{Int64}(1, :layer_1)
MultilayerVertex{Int64}(2, :layer_1)
MultilayerVertex{Int64}(3, :layer_1)
MultilayerVertex{Int64}(4, :layer_1)
MultilayerVertex{Int64}(5, :layer_1)
MultilayerVertex{Int64}(1, :layer_2)
MultilayerVertex{Int64}(2, :layer_2)
MultilayerVertex{Int64}(3, :layer_2)
MultilayerVertex{Int64}(4, :layer_2)
MultilayerVertex{Int64}(5, :layer_2)
To get a specific layer of a multilayer graph from its name, one may also write
layer_1 = get_layer(multilayergraph, :layer_1)
Same for interlayers
interlayer_2_1 = get_layer(multilayergraph, :interlayer_layer_2_layer_1)
Both MultilayerGraph
and MultilayerDiGraph
fully extend Graphs.jl
, so they have access to Graphs.jl API as one would expect, just keeping in mind that vertices are MultilayerVertex
s and not subtypes of Integer
(MultilayerVertex
is actually a subtype of AbstractVertex
that this package defines, see Future Developments), and that edges are MultilayerEdge
s, which actually subtypes AbstractEdge
.
Some notable examples are
edges(multilayergraph)
34-element Vector{MultilayerEdge}:
MultilayerEdge{MultilayerVertex{Int64}, Int64}(MultilayerVertex{Int64}(1, :layer_1), MultilayerVertex{Int64}(2, :layer_1), 1)
MultilayerEdge{MultilayerVertex{Int64}, Int64}(MultilayerVertex{Int64}(1, :layer_1), MultilayerVertex{Int64}(3, :layer_1), 1)
MultilayerEdge{MultilayerVertex{Int64}, Int64}(MultilayerVertex{Int64}(1, :layer_1), MultilayerVertex{Int64}(4, :layer_1), 1)
MultilayerEdge{MultilayerVertex{Int64}, Int64}(MultilayerVertex{Int64}(1, :layer_1), MultilayerVertex{Int64}(5, :layer_1), 1)
MultilayerEdge{MultilayerVertex{Int64}, Int64}(MultilayerVertex{Int64}(2, :layer_1), MultilayerVertex{Int64}(3, :layer_1), 1)
MultilayerEdge{MultilayerVertex{Int64}, Int64}(MultilayerVertex{Int64}(2, :layer_1), MultilayerVertex{Int64}(5, :layer_1), 1)
MultilayerEdge{MultilayerVertex{Int64}, Int64}(MultilayerVertex{Int64}(3, :layer_1), MultilayerVertex{Int64}(5, :layer_1), 1)
MultilayerEdge{MultilayerVertex{Int64}, Float64}(MultilayerVertex{Int64}(1, :layer_2), MultilayerVertex{Int64}(1, :layer_2), 0.7188425521261754)
MultilayerEdge{MultilayerVertex{Int64}, Float64}(MultilayerVertex{Int64}(2, :layer_2), MultilayerVertex{Int64}(3, :layer_2), 0.9012061650463197)
MultilayerEdge{MultilayerVertex{Int64}, Float64}(MultilayerVertex{Int64}(2, :layer_2), MultilayerVertex{Int64}(4, :layer_2), 0.6163304419976594)
MultilayerEdge{MultilayerVertex{Int64}, Float64}(MultilayerVertex{Int64}(3, :layer_2), MultilayerVertex{Int64}(5, :layer_2), 1.0046265072746847)
MultilayerEdge{MultilayerVertex{Int64}, Float64}(MultilayerVertex{Int64}(1, :layer_3), MultilayerVertex{Int64}(2, :layer_3), 0.2819477742859873)
MultilayerEdge{MultilayerVertex{Int64}, Float64}(MultilayerVertex{Int64}(2, :layer_3), MultilayerVertex{Int64}(4, :layer_3), 0.40111133874926597)
MultilayerEdge{MultilayerVertex{Int64}, Float64}(MultilayerVertex{Int64}(3, :layer_3), MultilayerVertex{Int64}(4, :layer_3), 0.9498077050078636)
MultilayerEdge{MultilayerVertex{Int64}, Float64}(MultilayerVertex{Int64}(1, :layer_3), MultilayerVertex{Int64}(5, :layer_3), 0.9618455695308973)
⋮
The implementation of MultilayerEdge
is
struct MultilayerEdge{ T <: MultilayerVertex, U <: Union{ <: Real, Nothing}} <: AbstractMultilayerEdge{T} # AbstractMultilayerEdge{T} subtypes AbstractEdge
src::T # The source vertex
dst::T # The destination vertex
weight::U # The edge weight. Can be `nothing` to signify an unweighted edge, or a Real
end
Other Example APIs
Let's showcase some other key functionalities.
Get the node type
eltype(multilayergraph)
Int64
Get the edge type
edgetype(multilayergraph)
MultilayerEdge{MultilayerVertex{Int64}, Float64}
Check whether an edge exists
has_edge(multilayergraph, MultilayerVertex(1, :layer_1), MultilayerVertex(4, :layer_2))
false
Remove an edge
rem_edge!
mimics the behaviour of the analogous function in Graphs.jl
rem_edge!(multilayergraph, MultilayerVertex(1, :layer_1), MultilayerVertex(2, :layer_2))
The multilayer doesn't have any edge between MultilayerVertex{Int64}(1, :layer_1) and MultilayerVertex{Int64}(2, :layer_2)
false
The message tells us that the edge was already non existent. In fact, if we check the adjacency_tensor
in the corresponding entry, we see that
multilayergraph.adjacency_tensor[1,2,1,2]
0.0
Add an edge
We may add an edge using the add_edge!
function. Since the interlayer we are adding the edge has an unweighted underlying graph (we will say that the interlayer is unweighted), we have to add an unweighted edge, so we don't specify many weights after the vertices. The adjacency tensor will be updated with a one(U)
in the correct position. add_edge!
mimics the behaviour of the analogous function in Graphs.jl
add_edge!(multilayergraph, MultilayerVertex(1, :layer_1), MultilayerVertex(2, :layer_2))
true
To add a weighted edge we just need to write
add_edge!(multilayergraph, MultilayerEdge(MultilayerVertex(1, :layer_1), MultilayerVertex(2, :layer_3), 3.14))
true
Get the inneighbors
To get all the inneighbors of a vertex we just need to write
inneighbors(multilayergraph, MultilayerVertex(1, :layer_1))
8-element Vector{MultilayerVertex{Int64}}:
MultilayerVertex{Int64}(2, :layer_1)
MultilayerVertex{Int64}(3, :layer_1)
MultilayerVertex{Int64}(4, :layer_1)
MultilayerVertex{Int64}(5, :layer_1)
MultilayerVertex{Int64}(1, :layer_3)
MultilayerVertex{Int64}(2, :layer_3)
MultilayerVertex{Int64}(4, :layer_3)
MultilayerVertex{Int64}(5, :layer_3)
outneighbors
would be analogous.
Get the global clustering coefficient
multilayer_global_clustering_coefficient(multilayergraph)
0.12667622867320932
Since our implementation of the global clustering coefficient follows De Domenico et al. (2013) rather than Graphs.jl
's implementation, we did not override Graphs.jl
's global_clustering_coefficient
, which works on MultilayerGraph
and MultilayerDiGraph
but yields different results. For details, consult ?multilayer_global_clustering_coefficient
or read the comments in the source code.
Multilayer-Specific Functions and Analysis
The following functions are specific to multilayer graphs or their implementations radically differ from their monoplex counterparts. For more information on every function, please refer to De Domenico et al. (2013) or consult the associated docstrings.
Overlay Monoplex Graph
Get the overlay monoplex graph: the monoplex graph whose nodes are the nodes of the multilayer graph and the edge between node $i$ and node $j$ has weight equal to the sum of all the weights of the edges between all vertex representations of $i$ and $j$ that belong to the same layer, for all the layers in the multilayer
get_overlay_monoplex_graph(multilayergraph)
{5, 8} undirected simple Int64 graph with Float64 weights
Depth-Weighted Clustering Coefficient
Get the global clustering coefficient where triplets are weighted by how many layers they span
w = [1/3, 1/3, 1/3]
multilayer_weighted_global_clustering_coefficient(multilayergraph,w)
0.12667622867320916
The first component of w
is the weight associated to triplets that are contained in one layer, the second component to triplets whose vertices are spread across exactly two layers, the third to triplets whose vertices are spread across exactly three layers. Weights must sum to `1.0 . When they are all equal (like in this example), the weighted global clustering coefficient coincides with the global clustering coefficient.
Eigenvector Centrality
Calculated via an iterative algorithm, its normalization is different from the Graphs.jl implementation. See ?eigenvector_centrality
for further details and context.
# The returned values are: the eigenvector centrality and the relative error at each iteration, that is, the summed absolute values of the componentwise differences between the centrality computed at the current iteration minus the centrality computed at the previous iteration.
eig_centrality, errs = eigenvector_centrality(multilayergraph; norm = "n", tol = 1e-3)
([0.260450377858897 0.02358226618172732 0.08408641909534659; 0.6517125919818912 0.646849109975291 0.25212976452498587; … ; 0.21160486454350666 0.24850764907380082 0.2809259415084613; 0.5089709123112656 0.27775286552140954 0.22663014660085468], [10.000000000000004, 0.7095737447337795, 0.329314405320397, 0.16256401870083886, 0.07860800793625616, 0.04125218689190476, 0.021191358681432643, 0.011056758224944913, 0.006831972142635288, 0.0035133270128280235, 0.0024675719418019056, 0.0013403531942171656, 0.0009739276503972424])
Modularity
Compute the modularity of the multilayer graph. The signature mimics the Graphs.jl modularity
implementation
modularity(multilayergraph,
rand([1, 2, 3, 4], length(nodes(multilayergraph)),length(multilayergraph.layers)) # communities
)
-0.039890139283044884
Von Neumann Entropy
Compute the Von Neumann entropy as presented in De Domenico et al. (2013).
von_neumann_entropy(multilayergraph)
3.3980014398404834
The Von Neumann entropy is currently available only for undirected multilayer graphs.
Other extended functions are: is_directed
, has_vertex
, ne
, nv
, outneighbors
, indegree
, outdegree
, degree
, mean_degree
, degree_second_moment
, degree_variance
, nn
, nodes
.
How to Contribute
If you wish to change or add some functionality, please file an issue.
How to Cite
If you use this package in your work, please cite this repository using the metadata in CITATION.bib
.
References
De Domenico et al. (2013) Mathematical Formulation of Multilayer Networks. Physical Review X