Skip to content

Commit 897e183

Browse files
authored
Add newman_watts_strogatz graph generator (#198)
In a later paper by Newman and Watts they generate networks that are slightly different from the original small-world networks by Watts and Strogatz. Instead of rewiring edges, they keep every original edge and add new long-range connections along the network.
1 parent 66409e3 commit 897e183

File tree

4 files changed

+88
-5
lines changed

4 files changed

+88
-5
lines changed

src/Graphs.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,7 @@ export
278278
erdos_renyi,
279279
expected_degree_graph,
280280
watts_strogatz,
281+
newman_watts_strogatz,
281282
random_regular_graph,
282283
random_regular_digraph,
283284
random_configuration_model,

src/SimpleGraphs/SimpleGraphs.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ export AbstractSimpleGraph,
5757
erdos_renyi,
5858
expected_degree_graph,
5959
watts_strogatz,
60+
newman_watts_strogatz,
6061
random_regular_graph,
6162
random_regular_digraph,
6263
random_configuration_model,

src/SimpleGraphs/generators/randgraphs.jl

Lines changed: 59 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -276,11 +276,11 @@ end
276276
watts_strogatz(n, k, β)
277277
278278
Return a [Watts-Strogatz](https://en.wikipedia.org/wiki/Watts_and_Strogatz_model)
279-
small world random graph with `n` vertices, each with expected degree `k`
279+
small world random graph with `n` vertices, each with expected degree `k`
280280
(or `k - 1` if `k` is odd). Edges are randomized per the model based on probability `β`.
281281
282282
The algorithm proceeds as follows. First, a perfect 1-lattice is constructed,
283-
where each vertex has exactly `div(k, 2)` neighbors on each side (i.e., `k` or
283+
where each vertex has exacly `div(k, 2)` neighbors on each side (i.e., `k` or
284284
`k - 1` in total). Then the following steps are repeated for a hop length `i` of
285285
`1` through `div(k, 2)`.
286286
@@ -296,6 +296,7 @@ For `β = 0`, the graph will remain a 1-lattice, and for `β = 1`, all edges wil
296296
be rewired randomly.
297297
298298
### Optional Arguments
299+
- `remove_edges=true`: if false, do not remove the original edges.
299300
- `is_directed=false`: if true, return a directed graph.
300301
- `rng=nothing`: set the Random Number Generator.
301302
- `seed=nothing`: set the RNG seed.
@@ -317,7 +318,8 @@ function watts_strogatz(
317318
n::Integer,
318319
k::Integer,
319320
β::Real;
320-
is_directed=false,
321+
is_directed::Bool=false,
322+
remove_edges::Bool=true,
321323
rng::Union{Nothing,AbstractRNG}=nothing,
322324
seed::Union{Nothing,Integer}=nothing,
323325
)
@@ -363,14 +365,66 @@ function watts_strogatz(
363365
d == s && continue # Self-loops prohibited
364366
d == t && break # Rewired to original target
365367
if add_edge!(g, s, d) # Was this valid (i.e., unconnected)?
366-
rem_edge!(g, s, t) # True rewiring: Delete original edge
367-
break # We found a valid target
368+
remove_edges && rem_edge!(g, s, t) # True rewiring: Delete original edge
369+
break # We found a valid target
368370
end
369371
end
370372
end
371373
return g
372374
end
373375

376+
"""
377+
newman_watts_strogatz(n, k, β)
378+
379+
Return a Newman-Watts-Strogatz small world random graph with `n` vertices, each
380+
with expected degree `k(1 + β)` (or `(k - 1)(1 + β)` if `k` is odd). Edges are
381+
randomized per the model based on probability `β`.
382+
383+
The algorithm proceeds as follows. First, a perfect 1-lattice is constructed,
384+
where each vertex has exacly `div(k, 2)` neighbors on each side (i.e., `k` or
385+
`k - 1` in total). Then the following steps are repeated for a hop length `i` of
386+
`1` through `div(k, 2)`.
387+
388+
1. Consider each vertex `s` in turn, along with the edge to its `i`th nearest
389+
neighbor `t`, in a clockwise sense.
390+
391+
2. Generate a uniformly random number `r`. If `r < β`, `s` is connected to some
392+
vertex `d`, chosen uniformly at random from the entire graph, excluding `s` and
393+
its neighbors. (Note that `t` is a valid candidate.)
394+
395+
For `β = 0`, the graph will remain a 1-lattice, and for `β = 1`, all edges will
396+
be rewired randomly.
397+
398+
Note: In the original paper by Newman and Watts, self-loops and double edges were
399+
allowed, which is not the case here. However, for large enough networks and small
400+
enough `p` and `k`, this should not deviate much from the original model.
401+
402+
### Optional Arguments
403+
- `is_directed=false`: if true, return a directed graph.
404+
- `rng=nothing`: set the Random Number Generator.
405+
406+
## Examples
407+
```jldoctest
408+
julia> newman_watts_strogatz(10, 4, 0.3)
409+
{10, 26} undirected simple Int64 graph
410+
411+
julia> newman_watts_strogatz(Int8(10), 4, 0.8, is_directed=true, seed=123)
412+
{10, 36} directed simple Int8 graph
413+
```
414+
415+
### References
416+
- Scaling and percolation in the small-world network model, M. E. J. Newman, Duncan J. Watts. [https://doi.org/10.1103/PhysRevE.60.7332](https://doi.org/10.1103/PhysRevE.60.7332)
417+
"""
418+
function newman_watts_strogatz(
419+
n::Integer,
420+
k::Integer,
421+
β::Real;
422+
is_directed::Bool=false,
423+
rng::Union{Nothing,AbstractRNG}=nothing,
424+
)
425+
return watts_strogatz(n, k, β; is_directed=is_directed, remove_edges=false, rng=rng)
426+
end
427+
374428
function _suitable(edges::Set{SimpleEdge{T}}, potential_edges::Dict{T,T}) where {T<:Integer}
375429
isempty(potential_edges) && return true
376430
list = keys(potential_edges)

test/simplegraphs/generators/randgraphs.jl

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,33 @@
8686
@test is_directed(ws) == true
8787
end
8888

89+
@testset "Newman-Watts-Strogatz" begin
90+
# Each iteration creates, on average, n*k/2*(1+β) edges (if the network is large enough).
91+
# As such, we need to average to check if the function works.
92+
nsamp = 100
93+
n = 1000
94+
β = 0.2
95+
k = 4
96+
mean_num_edges = n * k / 2 * (1 + β)
97+
nes = 0
98+
for i in 1:nsamp
99+
nws = newman_watts_strogatz(n, k, β; rng=rng)
100+
nes += ne(nws)
101+
@test nv(nws) == n
102+
@test is_directed(nws) == false
103+
end
104+
@test abs(sum(nes) / nsamp - mean_num_edges) / mean_num_edges < 0.01
105+
106+
nes = 0
107+
for i in 1:nsamp
108+
nws = newman_watts_strogatz(n, k, β; is_directed=true, rng=rng)
109+
nes += ne(nws)
110+
@test nv(nws) == n
111+
@test is_directed(nws) == true
112+
end
113+
@test abs(sum(nes) / nsamp - mean_num_edges) / mean_num_edges < 0.01
114+
end
115+
89116
@testset "Barabasi-Albert" begin
90117
ba = barabasi_albert(10, 2; rng=rng)
91118
@test nv(ba) == 10

0 commit comments

Comments
 (0)