diff --git a/docs/src/community.md b/docs/src/community.md index 2654978ef..306f8cd3f 100644 --- a/docs/src/community.md +++ b/docs/src/community.md @@ -19,6 +19,7 @@ Pages = [ "community/label_propagation.jl", "community/modularity.jl", "community/assortativity.jl" + "community/s_metric.jl" ] Private = false ``` diff --git a/src/LightGraphs.jl b/src/LightGraphs.jl index 221092db4..e0ef6eaa7 100644 --- a/src/LightGraphs.jl +++ b/src/LightGraphs.jl @@ -114,7 +114,7 @@ barabasi_albert!, static_fitness_model, static_scale_free, kronecker, dorogovtse #community modularity, core_periphery_deg, local_clustering,local_clustering_coefficient, global_clustering_coefficient, triangles, -label_propagation, maximal_cliques, clique_percolation, assortativity, +label_propagation, maximal_cliques, clique_percolation, assortativity,s_metric, #generators complete_graph, star_graph, path_graph, wheel_graph, cycle_graph, @@ -256,6 +256,7 @@ include("community/clustering.jl") include("community/cliques.jl") include("community/clique_percolation.jl") include("community/assortativity.jl") +include("community/s_metric.jl") include("spanningtrees/boruvka.jl") include("spanningtrees/kruskal.jl") include("spanningtrees/prim.jl") diff --git a/src/community/s_metric.jl b/src/community/s_metric.jl new file mode 100644 index 000000000..535ba7a87 --- /dev/null +++ b/src/community/s_metric.jl @@ -0,0 +1,35 @@ +""" + s_metric(g; norm=true) + +Return the normalised s-metric of `g`. + +The s-metric is defined as the sum of the product of degrees between pair of vertices +for every edge in `g`. [Ref](https://arxiv.org/abs/cond-mat/0501169) +In directed graphs, the paired values are the out-degree of source vertices +and the in-degree of destination vertices. +It is normalised by the maximum s_metric obtained from the family of graph +with similar degree distribution. s_max is computed from an approximation +formula as in https://journals.aps.org/pre/pdf/10.1103/PhysRevE.75.046102 +If `norm=false`, no normalisation is performed. + +# Examples +```jldoctest +julia> using LightGraphs + +julia> s_metric(star_graph(4)) +0.6 +``` +""" + +function s_metric(g::AbstractGraph{T}; norm=true) where T + s = zero(T) + for e in edges(g) + s += outdegree(g, src(e)) * indegree(g, dst(e)) + end + if norm + sm = sum(degree(g).^3)/2 + return s/sm + else + return s + end +end diff --git a/test/community/s_metric.jl b/test/community/s_metric.jl new file mode 100644 index 000000000..2b1a58dcb --- /dev/null +++ b/test/community/s_metric.jl @@ -0,0 +1,22 @@ +using Random, Statistics + +@testset "S-metric" begin + @testset "Directed ($seed)" for seed in [1, 2, 3], (_n, _ne) in [(14, 18), (10, 22), (7, 16)] + g = erdos_renyi(_n, _ne; is_directed=true, seed=seed) + sm = s_metric(g, norm=false) + sm2 = sum([outdegree(g, src(d)) * indegree(g, dst(d)) for d in edges(g)]) + @test @inferred sm ≈ sm2 + sm = s_metric(g, norm=true) + sm2 /= sum(degree(g).^3)/2 + @test @inferred sm ≈ sm2 + end + @testset "Undirected ($seed)" for seed in [1, 2, 3], (_n, _ne) in [(14, 18), (10, 22), (7, 16)] + g = erdos_renyi(_n, _ne; is_directed=false, seed=seed) + sm = s_metric(g, norm=false) + sm2 = sum([degree(g, src(d)) * degree(g, dst(d)) for d in edges(g)]) + @test @inferred sm ≈ sm2 + sm = s_metric(g, norm=true) + sm2 /= sum(degree(g).^3)/2 + @test @inferred sm ≈ sm2 + end +end diff --git a/test/runtests.jl b/test/runtests.jl index 295e400ef..025fc7f9f 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -11,13 +11,13 @@ using Statistics: mean const testdir = dirname(@__FILE__) -testgraphs(g) = is_directed(g) ? [g, DiGraph{UInt8}(g), DiGraph{Int16}(g)] : [g, Graph{UInt8}(g), Graph{Int16}(g)] +testgraphs(g) = is_directed(g) ? [g, DiGraph{UInt8}(g), DiGraph{Int16}(g)] : [g, Graph{UInt8}(g), Graph{Int16}(g)] testgraphs(gs...) = vcat((testgraphs(g) for g in gs)...) testdigraphs = testgraphs # some operations will create a large graph from two smaller graphs. We # might error out on very small eltypes. -testlargegraphs(g) = is_directed(g) ? [g, DiGraph{UInt16}(g), DiGraph{Int32}(g)] : [g, Graph{UInt16}(g), Graph{Int32}(g)] +testlargegraphs(g) = is_directed(g) ? [g, DiGraph{UInt16}(g), DiGraph{Int32}(g)] : [g, Graph{UInt16}(g), Graph{Int32}(g)] testlargegraphs(gs...) = vcat((testlargegraphs(g) for g in gs)...) tests = [ @@ -60,6 +60,7 @@ tests = [ "community/clustering", "community/clique_percolation", "community/assortativity", + "community/s_metric", "centrality/betweenness", "centrality/closeness", "centrality/degree",