-
Notifications
You must be signed in to change notification settings - Fork 93
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
Added edge-betweenness.jl to centralities #277
base: master
Are you sure you want to change the base?
Changes from 15 commits
86a8c4f
a6c4560
c56a445
10b31bc
b8d09d3
50b48c7
be10f80
2d79c3a
a77bc5f
45788ab
0d3630d
894b9f7
0b52ed8
00abd73
a4016b7
a1539e4
921d837
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
""" | ||
edge_betweenness_centrality(g, k) | ||
|
||
Compute the [edge betweenness centrality](https://en.wikipedia.org/wiki/Centrality#Betweenness_centrality) of an edge `e`. | ||
It is defined as the sum of the fraction of all-pairs shortest paths that pass through `e` | ||
`` | ||
bc(e) = \\sum_{s, t \\in V} | ||
\\frac{\\sigma_{st}(e)}{\\sigma_{st}} | ||
``. | ||
|
||
where `V`, is the set of nodes, \\frac{\\sigma_{st}} is the number of shortest-paths, and \\frac{\\sigma_{st}(e)} is the number of those paths passing through edge. | ||
|
||
### Optional Arguments | ||
- `normalize=true`: If true, normalize the betweenness values by the | ||
total number of possible distinct paths between all pairs in the graphs. | ||
For an undirected graph, this number is ``2/(|V|(|V|-1))`` | ||
and for a directed graph, ````1/(|V|(|V|-1))````. | ||
|
||
|
||
### References | ||
- Brandes 2001 & Brandes 2008 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can you give more details to help us check the algorithm? |
||
|
||
# Examples | ||
```jldoctest | ||
julia> using Graphs | ||
|
||
julia> Matrix(edge_betweenness_centrality(star_graph(5))) | ||
5×5 Matrix{Float64}: | ||
0.0 0.4 0.4 0.4 0.4 | ||
0.4 0.0 0.0 0.0 0.0 | ||
0.4 0.0 0.0 0.0 0.0 | ||
0.4 0.0 0.0 0.0 0.0 | ||
0.4 0.0 0.0 0.0 0.0 | ||
|
||
julia> Matrix(edge_betweenness_centrality(path_digraph(6), normalize=false)) | ||
6×6 Matrix{Float64}: | ||
0.0 5.0 0.0 0.0 0.0 0.0 | ||
0.0 0.0 8.0 0.0 0.0 0.0 | ||
0.0 0.0 0.0 9.0 0.0 0.0 | ||
0.0 0.0 0.0 0.0 8.0 0.0 | ||
0.0 0.0 0.0 0.0 0.0 5.0 | ||
0.0 0.0 0.0 0.0 0.0 0.0 | ||
""" | ||
function edge_betweenness_centrality( | ||
g::AbstractGraph, | ||
vs=vertices(g), | ||
distmx::AbstractMatrix=weights(g); | ||
normalize::Bool=true, | ||
) | ||
k = length(vs) | ||
edge_betweenness = spzeros(nv(g), nv(g)) | ||
for source in vs | ||
state = dijkstra_shortest_paths( | ||
g, source, distmx; allpaths=true, trackvertices=true | ||
) | ||
_accumulate_edges!(edge_betweenness, state) | ||
end | ||
_rescale_e!(edge_betweenness, nv(g), normalize, is_directed(g), k) | ||
|
||
return edge_betweenness | ||
end | ||
|
||
function edge_betweenness_centrality( | ||
gdalle marked this conversation as resolved.
Show resolved
Hide resolved
|
||
g::AbstractGraph, | ||
k::Integer, | ||
distmx::AbstractMatrix=weights(g); | ||
normalize=true, | ||
rng::Union{Nothing,AbstractRNG}=nothing, | ||
seed::Union{Nothing,Integer}=nothing, | ||
gdalle marked this conversation as resolved.
Show resolved
Hide resolved
|
||
) | ||
return edge_betweenness_centrality( | ||
g, | ||
sample(collect_if_not_vector(vertices(g)), k; rng=rng, seed=seed), | ||
distmx; | ||
normalize=normalize, | ||
) | ||
end | ||
|
||
function _accumulate_edges!( | ||
edge_betweenness::AbstractSparseMatrix, state::Graphs.AbstractPathState | ||
) | ||
σ = state.pathcounts | ||
pred = state.predecessors | ||
seen = state.closest_vertices | ||
δ = Dict(seen .=> 0.0) | ||
|
||
while length(seen) > 0 | ||
w = pop!(seen) | ||
|
||
coeff = (1.0 + δ[w]) / σ[w] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can you explain the dynamics of |
||
for v in pred[w] | ||
c = σ[v] * coeff | ||
edge_betweenness[v, w] += c | ||
δ[v] += c | ||
end | ||
end | ||
return nothing | ||
end | ||
|
||
function _rescale_e!( | ||
edge_betweenness::AbstractSparseMatrix, | ||
n::Integer, | ||
normalize::Bool, | ||
directed::Bool, | ||
k::Integer, | ||
) | ||
scale = n / k | ||
if normalize | ||
if n > 1 | ||
scale *= 1 / (n * (n - 1)) | ||
end | ||
if !directed | ||
scale *= 2 | ||
end | ||
end | ||
edge_betweenness .*= scale | ||
return nothing | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
|
||
@testset "Edge Betweenness" begin | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. how did you pick your test cases? |
||
rng = StableRNG(1) | ||
# self loops | ||
s1 = GenericGraph(SimpleGraph(Edge.([(1, 2), (2, 3), (3, 3)]))) | ||
s2 = GenericDiGraph(SimpleDiGraph(Edge.([(1, 2), (2, 3), (3, 3)]))) | ||
|
||
g3 = GenericGraph(path_graph(5)) | ||
|
||
@test @inferred(edge_betweenness_centrality(s1)) == | ||
sparse([1, 2, 3, 2], [2, 1, 2, 3], [2 / 3, 2 / 3, 2 / 3, 2 / 3], 3, 3) | ||
@test @inferred(edge_betweenness_centrality(s2)) == | ||
sparse([1, 2], [2, 3], [1 / 3, 1 / 3], 3, 3) | ||
|
||
g = GenericGraph(path_graph(2)) | ||
z = @inferred(edge_betweenness_centrality(g; normalize=true)) | ||
@test z[1, 2] == z[2, 1] == 1.0 | ||
z2 = @inferred(edge_betweenness_centrality(g, vertices(g))) | ||
z3 = @inferred(edge_betweenness_centrality(g, collect(vertices(g)))) | ||
@test z == z2 == z3 | ||
z = @inferred(edge_betweenness_centrality(g3; normalize=false)) | ||
@test z[1, 2] == z[5, 4] == 4.0 | ||
|
||
## | ||
# Weighted Graph tests | ||
g = GenericGraph(SimpleGraph(Edge.([(1, 2), (2, 3), (2, 5), (3, 4), (4, 5), (5, 6)]))) | ||
|
||
distmx = [ | ||
0.0 2.0 0.0 0.0 0.0 0.0 | ||
2.0 0.0 4.2 0.0 1.2 0.0 | ||
0.0 4.2 0.0 5.5 0.0 0.0 | ||
0.0 0.0 5.5 0.0 0.9 0.0 | ||
0.0 1.2 0.0 0.9 0.0 0.6 | ||
0.0 0.0 0.0 0.0 0.6 0.0 | ||
] | ||
|
||
@test isapprox( | ||
nonzeros(edge_betweenness_centrality(g, vertices(g), distmx; normalize=false)), | ||
[5.0, 5.0, 4.0, 8.0, 4.0, 1.0, 1.0, 4.0, 8.0, 4.0, 5.0, 5.0], | ||
) | ||
|
||
@test isapprox( | ||
nonzeros(edge_betweenness_centrality(g, vertices(g), distmx; normalize=true)), | ||
[5.0, 5.0, 4.0, 8.0, 4.0, 1.0, 1.0, 4.0, 8.0, 4.0, 5.0, 5.0] / | ||
(nv(g) * (nv(g) - 1)) * 2, | ||
) | ||
|
||
adjmx2 = [0 1 0; 1 0 1; 1 1 0] # digraph | ||
a2 = SimpleDiGraph(adjmx2) | ||
|
||
for g in test_generic_graphs(a2) | ||
distmx2 = [Inf 2.0 Inf; 3.2 Inf 4.2; 5.5 6.1 Inf] | ||
c2 = [0.24390243902439027, 0.27027027027027023, 0.1724137931034483] | ||
|
||
@test isapprox( | ||
nonzeros(edge_betweenness_centrality(g, vertices(g), distmx2; normalize=false)), | ||
[1.0, 1.0, 2.0, 1.0, 2.0], | ||
) | ||
|
||
@test isapprox( | ||
nonzeros(edge_betweenness_centrality(g, vertices(g), distmx2; normalize=true)), | ||
[1.0, 1.0, 2.0, 1.0, 2.0] * (1 / 6), | ||
) | ||
end | ||
# test #1405 / #1406 | ||
g = GenericGraph(grid([50, 50])) | ||
z = edge_betweenness_centrality(g; normalize=false) | ||
@test maximum(z) < nv(g) * (nv(g) - 1) | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
have you checked how this displays by building the docs?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.