Note that this code has been tested with Julia 1.5.1 and SimpleHypergraphs.jl version v0.1.13
.
using Pkg
pkg"add SimpleHypergraphs"
pkg"status SimpleHypergraphs"
The GraphPlot
package will make it possible to vizualize classic graph representations of a hypergraph such as bisection or 2-section representation.
pkg"add GraphPlot"
The second option is to use for visualisation the Python's HyperNetX. In order for this integration to work you need to install PyCall.jl
and Conda.jl
along with appropiate Python modules.
pkg"add PyCall Conda"
using PyCall
using Conda
Conda.runconda(`install matplotlib --yes`)
Conda.runconda(`install networkx --yes`)
run(`$(PyCall.python) -m pip install hypernetx`)`
# since the output of this command is long it is not included in this notebook
Include the library package with using.
using SimpleHypergraphs
Usually SimpleHypergraphs is used together with standard LightGraphs.jl library
import LightGraphs
We start by creating an empty hyperaph with 5 vertices and 4 hyperedges
h = Hypergraph{Float64}(5,4)
No we add the connection weights
h[1:3,1] .= 1.5
h[3,4] = 2.5
h[2,3] = 3.5
h[4,3:4] .= 4.5
h[5,4] = 5.5
h[5,2] = 6.5
h
#basic operations over the hg h
@assert add_vertex!(h) == 6
@assert add_hyperedge!(h) == 5
h[5,5] = 1.2
h[6,5] = 1.3
h
To visualize a given hypergraph h
, the user needs to specify two mandatory parameters:
h
to drawh
GraphBased
represents each hyperedge he
with a fake vertex fv
to which each vertex v ∈ he
is connected.HyperNetX
renders an Euler diagram of the hypergraph where vertices are black dots and hyper edges are convex shapes containing the vertices belonging to the edge set. GraphBased
visualization¶with_node_labels=true
, but node_labels
is not specified, vertex ids will be used as their label.SimpleHypergraphs.draw(h,
GraphBased;
width=500,
height=500,
radius=10, #same radius for each node
node_color = "yellow", #same color for each node
node_stroke="orange", #same stroke for each node
stroke_width=2, #same stroke-width value for each node
node_opacity=0.5, #same opacity for each node
with_node_labels=true, #wheter displaying or not node labels
with_node_metadata_hover=true,
)
SimpleHypergraphs.draw(
h,
GraphBased;
width=500,
height=500,
radius=10, #same radius for each node
node_color = "yellow", #same color for each node
node_colors = ["yellow", "yellow", "yellow", "blue", "red", "red", "blue"],
node_stroke = "orange", #same stroke for each node
node_strokes = ["orange", "orange", "orange", "orange", "black", "black", "black"],
stroke_width=2, #same stroke-width value for each node
node_opacity=0.5, #same opacity for each node
with_node_labels=true, #whether displaying or not node labels
node_labels=["A","B","C","D","E","F","G"],
with_node_metadata_hover=true,
)
with_node_weight=true
, each vertex weight within the hyperedges it belongs to will be displayed.SimpleHypergraphs.draw(
h,
GraphBased;
width=500,
height=500,
radius=10, #same radius for each node
node_color = "yellow", #same color for each node
node_stroke="orange", #same stroke for each node
stroke_width=2, #same stroke-width value for each node
node_opacity=0.5, #same opacity for each node
with_node_labels=true, #whether displaying or not node labels
node_labels=["A","B","C","D","E","F","G"],
with_node_metadata_hover=true,
with_node_weight=true
)
draw(
h,
GraphBased;
width=500,
height=500,
radius=10, #same radius for each node
node_color = "yellow", #same color for each node
node_stroke="orange", #same stroke for each node
stroke_width=2, #same stroke-width value for each node
node_opacity=0.5, #same opacity for each node
with_node_labels=true, #whether displaying or not node labels
with_node_metadata_hover=true,
with_node_weight=true, #whether displaying vertices metadata on mouse hover
he_colors=["green", "blue", "red", "yellow","black"], #hyperedges colors
with_he_labels=true, #whether displaying or not hyperedge labels
he_labels=["a","b","c","d"], #hyperedges labels
with_he_metadata_hover=true #whether displaying hyperedges metadata on mouse hover
)
SimpleHypergraphs integates the Python library HyperNetX to let the user visualize a hypergraph h
exploiting an Euler-diagram visualization. For more details, please refer to the library HyperNetX.
draw(h, HyperNetX; width=5, height=5, no_border=true)
There are many options for Hypergraph
plotting. Type ?draw
to see them all.
?draw # press Ctrl+Enter to see documentation for `draw`
The type BipartiteView
represents a non-materialized view of a bipartite representation hypergraph h
. Note this is a view - changes to the original hypergraph will be automatically reflected in the view.
The bipartite view of a hypergraph is suitable for processing with the LightGraphs.jl
package.
Several LightGraphs methods are provided for the compability.
b = BipartiteView(h)
The BipartiteView
provide LightGraphs.jl compability.
supertype(typeof(b))
We add here a edge to a parent Hypergraph of a bisection view. Note that this change will be reflected in the bipartite view
add_vertex!(h)
This graph can be plotted using LightGraphs
tools.
using GraphPlot
using LightGraphs
nodes, hyperedges = size(h)
nodes_membership = fill(1, nodes)
hyperedges_membership = fill(2, hyperedges)
membership = vcat(nodes_membership, hyperedges_membership)
nodecolor = ["lightseagreen", "orange"]
#membership color
nodefillc = nodecolor[membership]
gplot(b, nodefillc=nodefillc, nodelabel=1:LightGraphs.nv(b), layout=circular_layout)
The functionality of LightGraphs
can be used directly on a bipartite view of a hypergraph.
LightGraphs.a_star(b, 1, 3)
#number of vertices
LightGraphs.nv(b)
#number of edges
LightGraphs.ne(b)
#neighbors
sort(collect(LightGraphs.outneighbors(b,5)))
#neighbors
sort(collect(LightGraphs.inneighbors(b,9)))
#shortest path - it does not consider the nodes associated with a hyperedge
shortest_path(b,1,4)
Represents a two section view of a hypergraph h
. Note this is a view - changes to the original hypergraph will be automatically reflected in the view.
The bipartite view of a hypergraph is suitable for processing with the LightGraphs.jl
package.
Several LightGraphs methods are provided for the compability.
Note that the view will only work correctly for hypergraphs not having overlapping hyperedges. To check whether a graph has overlapping edges try has_overlapping_hedges(h) - for such graph you need to fully materialize it rather than use a view. This can be achieved via the get_twosection_adjacency_mx(h) method.
# This condition is required for an unmaterialized `TwoSectionView` representation of a hypergraph to make sense
@assert SimpleHypergraphs.has_overlapping_hedges(h) == false
t = TwoSectionView(h)
gplot(t, nodelabel=1:LightGraphs.nv(t))
#number of vertices
LightGraphs.nv(t)
#number of edges
LightGraphs.ne(t)
#neighbors
sort(collect(LightGraphs.outneighbors(t,5)))
#neighbors
sort(collect(LightGraphs.inneighbors(t,1)))
#shortest path
shortest_path(t,1,5)
Let us consider the following hypergraph
h = Hypergraph{Float64}(8,7)
h[1:3,1] .= 1.5
h[3,4] = 2.5
h[2,3] = 3.5
h[4,3:4] .= 4.5
h[5,4] = 5.5
h[5,2] = 6.5
h[5,5] = 5.5
h[5,6] = 6.5
h[6,7] = 5.5
h[7,7] = 6.5
h[8,7] = 6.5
h[8,6] = 6.5
h
Let us search for communities in the hypergraph h
best_comm = findcommunities(h, CFModularityCNMLike(100))
display(best_comm.bm)
display(best_comm.bp)
And now we visualize them in 2-section view
t = TwoSectionView(h)
function get_color(i, bp)
color = ["red","green","blue","yellow"]
for j in 1:length(bp)
if i in bp[j]
return color[j]
end
end
return "black"
end
gplot(t, nodelabel=1:LightGraphs.nv(t), nodefillc=get_color.(1:LightGraphs.nv(t), Ref(best_comm.bp) ))