Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Port] New s_metric method #28

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/src/community.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Pages = [
"community/label_propagation.jl",
"community/modularity.jl",
"community/assortativity.jl"
"community/s_metric.jl"
]
Private = false
```
3 changes: 2 additions & 1 deletion src/Graphs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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")
Expand Down
35 changes: 35 additions & 0 deletions src/community/s_metric.jl
Original file line number Diff line number Diff line change
@@ -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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
function s_metric(g::AbstractGraph{T}; norm=true) where T
function s_metric(g::AbstractGraph{T}; norm::Bool=true) where T

s = zero(T)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe zero(T) is not the right type here. For small graphs it might be something like Int8 that would overflow quite soon. Maybe it is best, to use Int here, or Float64, then the resulting type would be the same no matter if norm is set to true or false.

for e in edges(g)
s += outdegree(g, src(e)) * indegree(g, dst(e))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need some special handling for self-loops here?

end
if norm
sm = sum(degree(g).^3)/2
return s/sm
else
return s
end
end
22 changes: 22 additions & 0 deletions test/community/s_metric.jl
Original file line number Diff line number Diff line change
@@ -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)
Comment on lines +8 to +9
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
@test @inferred sm sm2
sm = s_metric(g, norm=true)
@test sm sm2
sm = @inferred s_metric(g, norm=true)

The inferred macro is used to check if a function is type stable, i.e. the compiler can infer its resulting argument. What you are testing here is, if is type stable, which is probably not what you want.

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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we have some tests here for graphs different from random graphs? I am especially thinking about graphs with self-loops and isolated vertices.

Also, it would be good to test with graphs of eltype different than Int.

5 changes: 3 additions & 2 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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 = [
Expand Down Expand Up @@ -60,6 +60,7 @@ tests = [
"community/clustering",
"community/clique_percolation",
"community/assortativity",
"community/s_metric",
"centrality/betweenness",
"centrality/closeness",
"centrality/degree",
Expand Down