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

Add random temporal hyperbolic graph #313

Merged
Merged
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
3 changes: 2 additions & 1 deletion src/GNNGraphs/GNNGraphs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,8 @@ export rand_graph,
rand_bipartite_heterograph,
knn_graph,
radius_graph,
rand_temporal_radius_graph
rand_temporal_radius_graph,
rand_temporal_hyperbolic_graph

include("sampling.jl")
export sample_neighbors
Expand Down
96 changes: 96 additions & 0 deletions src/GNNGraphs/generate.jl
Original file line number Diff line number Diff line change
Expand Up @@ -362,3 +362,99 @@ function rand_temporal_radius_graph(number_nodes::Int,
end
return TemporalSnapshotsGNNGraph(tg)
end


function _hyperbolic_distance(nodeA::Array{Float64, 1},nodeB::Array{Float64, 1}; ζ::Real)
if nodeA != nodeB
a = cosh(ζ * nodeA[1]) * cosh(ζ * nodeB[1])
b = sinh(ζ * nodeA[1]) * sinh(ζ * nodeB[1])
c = cos(pi - abs(pi - abs(nodeA[2] - nodeB[2])))
d = acosh(a - (b * c)) / ζ
else
d = 0.0
end
return d
end

"""
rand_temporal_hyperbolic_graph(number_nodes::Int,
number_snapshots::Int;
α::Real,
R::Real,
speed::Real,
ζ::Real=1,
self_loop = false,
kws...)

Create a random temporal graph given `number_nodes` nodes and `number_snapshots` snapshots.
First, the positions of the nodes are generated with a quasi-uniform distribution (depending on the parameter `α`) in hyperbolic space within a disk of radius `R`. Two nodes are connected if their hyperbolic distance is less than `R`. Each following snapshot is created in order to keep the same initial distribution.
Copy link
Member

Choose a reason for hiding this comment

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

What does quasi-uniform mean?

Also, "each following snapshot is created in order to keep the same initial distribution." is not clear

Copy link
Member Author

Choose a reason for hiding this comment

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

The term quasi-uniform is used in the paper, but it has no formal definition. For a certain value of the parameter alpha the distribution is uniform, for others it is not. In the paper, quasi-uniform refers informally to the values of the parameter that are close to those for which the distribution is uniform.

The positions of the points in each snapshot are updated so that they are distributed with the same distribution as in the first snapshot (the marginal distribution is the same for each snapshot).


# Arguments

- `number_nodes`: The number of nodes of each snapshot.
- `number_snapshots`: The number of snapshots.
- `α`: The parameter that controls the position of the points. If `α=ζ`, the points are uniformly distributed on the disk of radius `R`. If `α>ζ`, the points are more concentrated in the center of the disk. If `α<ζ`, the points are more concentrated at the boundary of the disk.
- `R`: The radius of the disk and of connection.
- `speed`: The speed to update the nodes.
- `ζ`: The parameter that controls the curvature of the disk.
- `self_loops`: If `true`, consider the node itself among its neighbors, in which
case the graph will contain self-loops.
- `kws`: Further keyword arguments will be passed to the [`GNNGraph`](@ref) constructor of each snapshot.

# Example

```julia-repl
julia> n, snaps, α, R, speed, ζ = 10, 5, 1.0, 4.0, 0.1, 1.0;

julia> thg = rand_temporal_hyperbolic_graph(n, snaps; α, R, speed, ζ)
TemporalSnapshotsGNNGraph:
num_nodes: [10, 10, 10, 10, 10]
num_edges: [44, 46, 48, 42, 38]
num_snapshots: 5
```

# References
Section D of the paper [Dynamic Hidden-Variable Network Models](https://arxiv.org/pdf/2101.00414.pdf) and the paper
[Hyperbolic Geometry of Complex Networks](https://arxiv.org/pdf/1006.5169.pdf)
"""
function rand_temporal_hyperbolic_graph(number_nodes::Int,
number_snapshots::Int;
α::Real,
R::Real,
speed::Real,
ζ::Real=1,
self_loop = false,
kws...)
@assert number_snapshots > 1 "The number of snapshots must be greater than 1"
@assert α > 0 "α must be greater than 0"

probabilities = rand(number_nodes)

points = Array{Float64}(undef,2,number_nodes)
points[1,:].= (1/α) * acosh.(1 .+ (cosh(α * R) - 1) * probabilities)
points[2,:].= 2 * pi * rand(number_nodes)

tg = Vector{GNNGraph}(undef, number_snapshots)

for time in 1:number_snapshots
adj = zeros(number_nodes,number_nodes)
for i in 1:number_nodes
for j in 1:number_nodes
if !self_loop && i==j
continue
elseif _hyperbolic_distance(points[:,i],points[:,j]; ζ) <= R
adj[i,j] = adj[j,i] = 1
end
end
end
tg[time] = GNNGraph(adj)

probabilities .= probabilities .+ (2 * speed * rand(number_nodes) .- speed)
probabilities[probabilities.>1] .= 1 .- (probabilities[probabilities .> 1] .% 1)
probabilities[probabilities.<0] .= abs.(probabilities[probabilities .< 0])

points[1,:].= (1/α) * acosh.(1 .+ (cosh(α * R) - 1) * probabilities)
points[2,:].= points[2,:] .+ (2 * speed * rand(number_nodes) .- speed)
end
return TemporalSnapshotsGNNGraph(tg)
end
15 changes: 15 additions & 0 deletions test/GNNGraphs/generate.jl
Original file line number Diff line number Diff line change
Expand Up @@ -104,4 +104,19 @@ end
r2 = 0.95
tg2 = rand_temporal_radius_graph(number_nodes, number_snapshots, speed, r2)
@test mean(mean(degree.(tg.snapshots)))<=mean(mean(degree.(tg2.snapshots)))
end

@testset "rand_temporal_hyperbolic_graph" begin
@test GraphNeuralNetworks.GNNGraphs._hyperbolic_distance([1.0,1.0],[1.0,1.0];ζ=1)==0
@test GraphNeuralNetworks.GNNGraphs._hyperbolic_distance([0.23,0.11],[0.98,0.55];ζ=1)==GraphNeuralNetworks.GNNGraphs._hyperbolic_distance([0.98,0.55],[0.23,0.11];ζ=1)
number_nodes = 30
number_snapshots = 5
α, R, speed, ζ = 1, 1, 0.1, 1

tg = rand_temporal_hyperbolic_graph(number_nodes, number_snapshots; α, R, speed, ζ)
@test tg.num_nodes == [number_nodes for i in 1:number_snapshots]
@test tg.num_snapshots == number_snapshots
R = 10
tg1 = rand_temporal_hyperbolic_graph(number_nodes, number_snapshots; α, R, speed, ζ)
@test mean(mean(degree.(tg1.snapshots)))<=mean(mean(degree.(tg.snapshots)))
end