Skip to content

Commit 739a016

Browse files
authored
Merge pull request #55 from JuliaGraphs/default_layer_constructors
General improvements to the user experience
2 parents 72c18d2 + a593377 commit 739a016

24 files changed

+3432
-636
lines changed

Project.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ IterTools = "c8e1da08-722c-5040-9ed9-7db0dc04731e"
1313
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
1414
MetaGraphs = "626554b9-1ddb-594c-aa3c-2596fe9399a5"
1515
OMEinsum = "ebe7aa44-baf0-506c-a96f-8464559b3922"
16+
PrettyTables = "08abe8d2-0d0c-5749-adfa-8a2ac140af0d"
1617
SimpleTraits = "699a6c99-e7fa-54fc-8d76-47d257e15c1d"
1718
SimpleValueGraphs = "b43c691f-cac2-5415-8122-396fe16a49fc"
1819
SimpleWeightedGraphs = "47aef6b3-ad0c-573a-a1e2-d07658019622"

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,10 @@ In the package documentation you can find a [tutorial](https://juliagraphs.org/M
5454
- [ ] [Implement more general configuration models / graph generators](https://github.com/JuliaGraphs/MultilayerGraphs.jl/issues/33);
5555
- [ ] [Implement graph of layers](https://github.com/JuliaGraphs/MultilayerGraphs.jl/issues/34);
5656
- [ ] [Implement projected monoplex and overlay graphs](https://github.com/JuliaGraphs/MultilayerGraphs.jl/issues/35);
57-
- [ ] [Implement more default multilayer graphs](https://github.com/JuliaGraphs/MultilayerGraphs.jl/issues/36) (e.g. multiplex graphs).
57+
- [ ] [Implement more default multilayer graphs](https://github.com/JuliaGraphs/MultilayerGraphs.jl/issues/36) (e.g. multiplex graphs);
58+
- [ ] [Implement configuration models / graph generators for interlayers](https://github.com/JuliaGraphs/MultilayerGraphs.jl/issues/46);
59+
- [ ] [Relax the requirement of same `T` and `U` for all `Layer`s and `Interlayer`s that are meant to constitute a `Multilayer(Di)Graph`](https://github.com/JuliaGraphs/MultilayerGraphs.jl/issues/53);
60+
- [ ] [Implement multilayer graph data visualisation functionalities (or a new package)](https://github.com/JuliaGraphs/MultilayerGraphs.jl/issues/54).
5861

5962
## How to Contribute
6063

docs/src/API.md

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,16 @@ Layer(
5050
allow_self_loops::Bool = false
5151
) where {T<:Integer, U <: Real, G<:AbstractGraph{T}}
5252
53+
layer_simplegraph
54+
layer_simpledigraph
55+
layer_simpleweightedgraph
56+
layer_simpleweighteddigraph
57+
layer_metadigraph
58+
layer_valgraph
59+
layer_valoutdigraph
60+
layer_valdigraph
61+
layer_metagraph
62+
5363
has_node(layer::Layer, n::Node)
5464
add_vertex!(layer::Layer, mv::MultilayerVertex)
5565
add_vertex!(layer::L, n::Node, args...; kwargs...) where {T, L <: Layer{T}}
@@ -80,6 +90,16 @@ Interlayer(
8090
transfer_vertex_metadata::Bool = false
8191
) where {T<:Integer, U <: Union{Nothing, <: Real}, G<:AbstractGraph{T}}
8292
93+
interlayer_simplegraph
94+
interlayer_simpleweightedgraph
95+
interlayer_metagraph
96+
interlayer_valgraph
97+
interlayer_simpledigraph
98+
interlayer_simpleweighteddigraph
99+
interlayer_metadigraph
100+
interlayer_valoutdigraph
101+
interlayer_valdigraph
102+
83103
84104
multiplex_interlayer(
85105
layer_1::Layer{T,U},
@@ -112,6 +132,7 @@ mv_outneighbors(subgraph::AbstractSubGraph, mv::MultilayerVertex)
112132
mv_neighbors(subgraph::AbstractSubGraph, mv::MultilayerVertex)
113133
has_edge(subgraph::AbstractSubGraph,me::MultilayerEdge)
114134
has_edge( subgraph::AbstractSubGraph, s::MultilayerVertex, d::MultilayerVertex)
135+
has_edge( layer::Layer, s::MultilayerVertex{nothing}, d::MultilayerVertex{nothing})
115136
ne(subgraph::AbstractSubGraph)
116137
edges(subgraph::S) where {T,U,S<:AbstractSubGraph{T,U}}
117138
add_edge!( subgraph::S, me::E) where {T,U<:Real,S<:AbstractSubGraph{T,U},E<:MultilayerEdge{ <: Union{U, Nothing}}}
@@ -126,14 +147,16 @@ is_directed(subgraph::AbstractSubGraph)
126147
is_directed(::Type{S}) where {T,U,G,S <: AbstractSubGraph{T,U,G}}
127148
adjacency_matrix(subgraph::AbstractSubGraph)
128149
MultilayerGraphs.weights(subgraph::S) where {T,U,S<:AbstractSubGraph{T,U}}
129-
name(subgraph::AbstractSubGraph)
130-
150+
131151
is_multiplex_interlayer(interlayer::Interlayer)
132152
133153
get_symmetric_interlayer(
134154
interlayer::In;
135155
symmetric_interlayer_name::String = String(interlayer.name) * "_rev"
136156
) where {T,U,G,In<:Interlayer{T,U,G}}
157+
158+
name(subgraph::AbstractSubGraph)
159+
graph(subgraph::AbstractSubGraph)
137160
```
138161

139162
### [Multilayer-Specific Methods](@id msm_eu)

docs/src/index.md

Lines changed: 79 additions & 30 deletions
Large diffs are not rendered by default.

src/MultilayerGraphs.jl

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
# The δ_Ω implementation could be moved to a separate file.
1616

17-
# The usage of mutable `MissingVertex`s (although limited to SupraWeightMatrix) points to the fact that Bijections (at least as they are used right now) are not the best way to represent integer label-MultilayerVertex associations. We my implement our own container object or use the exixtsing ons differently.
17+
# The usage of mutable `MissingVertex`s (although limited to SupraWeightMatrix) points to the fact that Bijections (at least as they are used right now) are not the best way to represent integer label-MultilayerVertex associations. We my implement our own container object or use the existing ons differently.
1818

1919
# We need a quick Multilayer(Di)Graph constructor of the form Multilayer(Di)Graph(nn, nl; nv = rand(0:nn*nl), ne = rand(0:nv*(nv-1)) kwargs...) where kwargs may be used to further specify it.
2020

@@ -59,6 +59,7 @@ export
5959
mv_outneighbors,
6060
neighbors,
6161
mv_neighbors,
62+
get_v,
6263
edgetype,
6364
has_edge,
6465
ne,
@@ -71,15 +72,34 @@ export
7172
adjacency_matrix,
7273
weights,
7374
name,
75+
graph,
7476
# layer.jl
7577
AbstractLayer,
7678
Layer,
79+
layer_simplegraph,
80+
layer_simpledigraph,
81+
layer_simpleweightedgraph,
82+
layer_simpleweighteddigraph,
83+
layer_metadigraph,
84+
layer_valgraph,
85+
layer_valoutdigraph,
86+
layer_valdigraph,
87+
layer_metagraph,
7788
has_node,
7889
add_vertex!,
7990
rem_vertex!,
8091
# interlayer.jl
8192
AbstractInterlayer,
8293
Interlayer,
94+
interlayer_simplegraph,
95+
interlayer_simpleweightedgraph,
96+
interlayer_metagraph,
97+
interlayer_valgraph,
98+
interlayer_simpledigraph,
99+
interlayer_simpleweighteddigraph,
100+
interlayer_metadigraph,
101+
interlayer_valoutdigraph,
102+
interlayer_valdigraph,
83103
multiplex_interlayer,
84104
empty_interlayer,
85105
is_multiplex_interlayer,
@@ -151,7 +171,7 @@ export
151171
kleitman_wang_graph_generator
152172
# tensorfacoriazations.jl
153173

154-
using Base, InteractiveUtils, IterTools, SimpleTraits, Bijections
174+
using Base, InteractiveUtils, IterTools, SimpleTraits, Bijections, PrettyTables
155175
using Distributions: Uniform
156176
using LinearAlgebra, Statistics, OMEinsum, TensorOperations, Distributions
157177
using DataStructures, SparseArrays
@@ -163,6 +183,7 @@ include("vertices/multilayervertex.jl")
163183
include("vertices/missingvertex.jl")
164184
include("multilayeredge.jl")
165185
include("halfedge.jl")
186+
include("subgraphs/abstractdescriptor.jl")
166187
include("subgraphs/layerdescriptor.jl")
167188
include("subgraphs/interlayerdescriptor.jl")
168189
include("subgraphs/abstractsubgraph.jl")

src/abstractmultilayergraph.jl

Lines changed: 58 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -496,8 +496,8 @@ function _specify_interlayer!(
496496
new_interlayer.descriptor
497497

498498
for edge in edges(new_interlayer)
499-
success = add_edge!(mg, edge)
500-
@assert success
499+
@assert add_edge!(mg, edge)
500+
# assert success
501501
end
502502

503503
return true
@@ -1188,9 +1188,64 @@ function get_rich_mv(
11881188
) where {T,U,M<:AbstractMultilayerGraph{T,U}}
11891189
if perform_checks
11901190
haskey(mg.v_V_associations, i) ||
1191-
throw(ErrorException("$i is not a vertex of the multilayer graph"))
1191+
throw(ErrorException("$i is not a vertex of the multilayer graph."))
11921192
end
11931193

11941194
bare_V = mg.v_V_associations[i]
11951195
return MV(bare_V.node, bare_V.layer, mg.v_metadata_dict[i])
11961196
end
1197+
1198+
# Console print utilities
1199+
function to_string(x::AbstractMultilayerGraph)
1200+
unionall_type = typeof(x).name.wrapper
1201+
parameters = typeof(x).parameters
1202+
1203+
layers_names = name.(x.layers)
1204+
layers_underlying_graphs = typeof.(graph.(x.layers))
1205+
1206+
layers_table = pretty_table(
1207+
String,
1208+
hcat(layers_names, layers_underlying_graphs);
1209+
title="### LAYERS",
1210+
header=(["NAME", "UNDERLYING GRAPH"]),
1211+
alignment=:c,
1212+
header_alignment=:c,
1213+
header_crayon=crayon"yellow bold",
1214+
hlines=:all,
1215+
)
1216+
1217+
interlayers_names = name.(values(x.interlayers))
1218+
interlayers_underlying_graphs = typeof.(graph.(values(x.interlayers)))
1219+
interlayer_layer_1s = getproperty.(values(x.interlayers), Ref(:layer_1))
1220+
interlayer_layer_2s = getproperty.(values(x.interlayers), Ref(:layer_2))
1221+
interlayer_tranfers =
1222+
getproperty.(values(x.interlayers), Ref(:transfer_vertex_metadata))
1223+
1224+
interlayers_table = pretty_table(
1225+
String,
1226+
hcat(
1227+
interlayers_names,
1228+
interlayer_layer_1s,
1229+
interlayer_layer_2s,
1230+
interlayers_underlying_graphs,
1231+
interlayer_tranfers,
1232+
);
1233+
title="### INTERLAYERS",
1234+
header=([
1235+
"NAME", "LAYER 1", "LAYER 2", "UNDERLYING GRAPH", "TRANSFER VERTEX METADATA"
1236+
]),
1237+
alignment=:c,
1238+
header_alignment=:c,
1239+
header_crayon=crayon"yellow bold",
1240+
hlines=:all,
1241+
)
1242+
1243+
return """
1244+
`$unionall_type` with vertex type `$(parameters[1])` and weight type `$(parameters[2])`.
1245+
1246+
$layers_table
1247+
1248+
$interlayers_table
1249+
"""
1250+
end
1251+
Base.show(io::IO, x::AbstractMultilayerGraph) = print(io, to_string(x))

src/abstractmultilayerugraph.jl

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -272,13 +272,9 @@ function get_subgraph(
272272
v for (v, mv) in collect(mg.v_V_associations) if mv.layer == descriptor.name
273273
])
274274

275-
_vertices = get_rich_mv.(Ref(mg), vs)#MultilayerVertex{descriptor.name}[]
275+
_vertices = get_rich_mv.(Ref(mg), vs)
276276

277-
#= for (v,mv) in zip(vs, getindex.(Ref(mg.v_V_associations),vs))
278-
push!(_vertices, get_rich_mv(mg, v)) #MV(mv.node, mv.layer, mg.v_metadata_dict[v] )
279-
end
280-
=#
281-
edge_list = MultilayerEdge{U}[]#MultilayerEdge{U}[MultilayerEdge(src, vertex(halfedge), weight(halfedge), metadata(halfedge)) for (src,halfedge) in zip(_vertices,mg.fadjlist[vs]) if vertex(halfedge).layer == descriptor.name && get_v(mg, vertex(halfedge)) >= get_v(mg,src) ]
277+
edge_list = MultilayerEdge{U}[]
282278

283279
for (src_v, halfedges_from_src) in zip(vs, getindex.(Ref(mg.fadjlist), vs))
284280
src_bare_V = mg.v_V_associations[src_v]

src/graphs_extensions/metagraphs.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,9 @@ function _add_edge!(
6363
) where {T<:Integer,W<:Union{<:Real,Nothing}}
6464
(isnothing(weight) || weight == 1.0) ||
6565
@warn "Trying to add a weighted edge to an unweighted graph of type $(typeof(g)). Weight $weight will be ignored."
66-
return add_edge!(g, src, dst, Dict(key => value for (key, value) in pairs(metadata)))
66+
return add_edge!(
67+
g, src, dst, Dict(Symbol(pair.first) => pair.second for pair in pairs(metadata))
68+
) #Symbol(string(key)) => value for (key, value) in pairs(metadata)
6769
end
6870

6971
function _get_edge_weight(

src/halfedge.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ vertex(he::HalfEdge) = he.vertex
3232
3333
Return the weight of the edge.
3434
"""
35-
weight(he::HalfEdge) = he.weight
35+
SimpleWeightedGraphs.weight(he::HalfEdge) = he.weight
3636
"""
3737
metadata(he::HalfEdge)
3838

src/multilayerdigraph.jl

Lines changed: 8 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ mutable struct MultilayerDiGraph{T,U} <: AbstractMultilayerDiGraph{T,U}
99
v_V_associations::Bijection{T,<:MultilayerVertex} # A Bijection from Bijections.jl that associates numeric vertices to `MultilayerVertex`s.
1010
idx_N_associations::Bijection{Int64,Node} # A Bijection from Bijections.jl that associates Int64 to `Node`s.
1111
fadjlist::Vector{Vector{HalfEdge{<:MultilayerVertex,<:Union{Nothing,U}}}} # the forward adjacency list of the MultilayerDiGraph. It is a vector of vectors of `HalfEdge`s. Its i-th element are the `HalfEdge`s that originate from `v_V_associations[i]`.
12-
badjlist::Vector{Vector{HalfEdge{<:MultilayerVertex,<:Union{Nothing,U}}}} # the bacward adjacency list of the MultilayerDiGraph. It is a vector of vectors of `HalfEdge`s. Its i-th element are the `HalfEdge`s that insost on `v_V_associations[i]`.
12+
badjlist::Vector{Vector{HalfEdge{<:MultilayerVertex,<:Union{Nothing,U}}}} # the backward adjacency list of the MultilayerDiGraph. It is a vector of vectors of `HalfEdge`s. Its i-th element are the `HalfEdge`s that insist on `v_V_associations[i]`.
1313
v_metadata_dict::Dict{T,<:Union{<:Tuple,<:NamedTuple}} # A Dictionary that associates numeric vertices to their metadata
1414
end
1515

@@ -162,12 +162,6 @@ function MultilayerDiGraph(
162162
),
163163
)
164164

165-
minimum(support(indegree_distribution)) >= 0 || throw(
166-
ErrorException(
167-
"Both the `indegree_distribution` and the `outdegree_distribution` must have positive support. Found $(support(indegree_distribution)) and $(support(outdegree_distribution)).",
168-
),
169-
)
170-
171165
empty_multilayerdigraph = MultilayerDiGraph(
172166
empty_layers,
173167
empty_interlayers;
@@ -177,23 +171,9 @@ function MultilayerDiGraph(
177171

178172
n = nv(empty_multilayerdigraph)
179173

180-
indegree_sequence = Vector{Int64}(undef, n)
181-
outdegree_sequence = Vector{Int64}(undef, n)
182-
acceptable = false
183-
184-
@info "Trying to sample a digraphical sequence from the two provided distributions..."
185-
while !acceptable
186-
indegree_sequence .=
187-
round.(Ref(Int), rand(indegree_distribution, nv(empty_multilayerdigraph)))
188-
outdegree_sequence .=
189-
round.(Ref(Int), rand(outdegree_distribution, nv(empty_multilayerdigraph)))
190-
191-
acceptable = all(0 .<= vcat(indegree_sequence, outdegree_sequence) .< n)
192-
193-
if acceptable
194-
acceptable = isdigraphical(indegree_sequence, outdegree_sequence)
195-
end
196-
end
174+
indegree_sequence, outdegree_sequence = sample_digraphical_degree_sequences(
175+
indegree_distribution, outdegree_distribution, n
176+
)
197177

198178
return MultilayerDiGraph(
199179
empty_multilayerdigraph,
@@ -223,7 +203,7 @@ function MultilayerDiGraph(
223203
perform_checks::Bool=false,
224204
) where {T,U}
225205
(allow_self_loops && perform_checks) &&
226-
@warn "Checks for graphicality and coherence with the provided `empty_multilayerdigraph` are currently performed without taking into account self-loops. Thus said checks may fail event though the provided `indegree_sequence` and `outdegree_sequence` may be graphical when one allows for self-loops within the directed multilayer graph to be present. If you are sure that the provided `indegree_sequence` and `outdegree_sequence` are indeed graphical under those circumstances, you may want to disable checks by setting `perform_checks = false`. We apologize for the inconvenient."
206+
@warn "Checks for graphicality and coherence with the provided `empty_multilayerdigraph` are currently performed without taking into account self-loops. Thus said checks may fail event though the provided `indegree_sequence` and `outdegree_sequence` may be graphical when one allows for self-loops within the directed multilayer graph to be present. If you are sure that the provided `indegree_sequence` and `outdegree_sequence` are indeed graphical under those circumstances, you may want to disable checks by setting `perform_checks = false`. We apologize for the inconvenience."
227207

228208
_multilayerdigraph = deepcopy(empty_multilayerdigraph)
229209

@@ -237,19 +217,19 @@ function MultilayerDiGraph(
237217
n = nv(_multilayerdigraph)
238218
n == length(indegree_sequence) == length(outdegree_sequence) || throw(
239219
ErrorException(
240-
"The number of vertices of the provided empty MultilayerDiGraph does not match the length of the `indegree_sequence` or the `outdegree_sequence`. Found $(nv(_multilayerdigraph)) , $(length(indegree_sequence)) and $(length(outdegree_sequence))",
220+
"The number of vertices of the provided empty MultilayerDiGraph does not match the length of the `indegree_sequence` or the `outdegree_sequence`. Found $(nv(_multilayerdigraph)) , $(length(indegree_sequence)) and $(length(outdegree_sequence)).",
241221
),
242222
)
243223

244224
sum(indegree_sequence) == sum(outdegree_sequence) || throw(
245225
ErrorException(
246-
"The sum of the `indegree_sequence` and the `outdegree_sequence` must match. Found $(sum(indegree_sequence)) and $(sum(outdegree_sequence))",
226+
"The sum of the `indegree_sequence` and the `outdegree_sequence` must match. Found $(sum(indegree_sequence)) and $(sum(outdegree_sequence)).",
247227
),
248228
)
249229

250230
isdigraphical(degree_sequence) || throw(
251231
ArgumentError(
252-
"`indegree_sequence` and `outdegree_sequence` must be digraphical"
232+
"`indegree_sequence` and `outdegree_sequence` must be digraphical."
253233
),
254234
)
255235
end

0 commit comments

Comments
 (0)