Temporal Graphs
Temporal graphs are graphs with time-varying topologies and features. In GNNGraphs.jl, they are represented by the TemporalSnapshotsGNNGraph type.
Creating a TemporalSnapshotsGNNGraph
A temporal graph can be created by passing a list of snapshots to the constructor. Each snapshot is a GNNGraph.
julia> using GNNGraphs
julia> snapshots = [rand_graph(10, 20) for i in 1:5];
julia> tg = TemporalSnapshotsGNNGraph(snapshots)
TemporalSnapshotsGNNGraph:
num_nodes: [10, 10, 10, 10, 10]
num_edges: [20, 20, 20, 20, 20]
num_snapshots: 5A new temporal graph can be created by adding or removing snapshots to an existing temporal graph.
julia> new_tg = add_snapshot(tg, 3, rand_graph(10, 16)) # add a new snapshot at time 3
TemporalSnapshotsGNNGraph:
num_nodes: [10, 10, 10, 10, 10, 10]
num_edges: [20, 20, 16, 20, 20, 20]
num_snapshots: 6julia> snapshots = [rand_graph(10,20), rand_graph(10,14), rand_graph(10,22)];
julia> tg = TemporalSnapshotsGNNGraph(snapshots)
TemporalSnapshotsGNNGraph:
num_nodes: [10, 10, 10]
num_edges: [20, 14, 22]
num_snapshots: 3
julia> new_tg = remove_snapshot(tg, 2) # remove snapshot at time 2
TemporalSnapshotsGNNGraph:
num_nodes: [10, 10]
num_edges: [20, 22]
num_snapshots: 2See rand_temporal_radius_graph and rand_temporal_hyperbolic_graph for generating random temporal graphs.
julia> tg = rand_temporal_radius_graph(10, 3, 0.1, 0.5)
TemporalSnapshotsGNNGraph:
num_nodes: [10, 10, 10]
num_edges: [32, 30, 34]
num_snapshots: 3Indexing
Snapshots in a temporal graph can be accessed using indexing:
julia> snapshots = [rand_graph(10, 20), rand_graph(10, 14), rand_graph(10, 22)];
julia> tg = TemporalSnapshotsGNNGraph(snapshots)
TemporalSnapshotsGNNGraph:
num_nodes: [10, 10, 10]
num_edges: [20, 14, 22]
num_snapshots: 3
julia> tg[1] # first snapshot
GNNGraph:
num_nodes: 10
num_edges: 20
julia> tg[2:3] # snapshots 2 and 3
TemporalSnapshotsGNNGraph:
num_nodes: [10, 10]
num_edges: [14, 22]
num_snapshots: 2A snapshot can be modified by assigning a new snapshot to the temporal graph:
julia> tg[1] = rand_graph(10, 16) # replace first snapshot
GNNGraph:
num_nodes: 10
num_edges: 16Iteration and Broadcasting
Iteration and broadcasting over a temporal graph is similar to that of a vector of snapshots:
julia> snapshots = [rand_graph(10, 20), rand_graph(10, 14), rand_graph(10, 22)];
julia> tg = TemporalSnapshotsGNNGraph(snapshots);
julia> [g for g in tg] # iterate over snapshots
3-element Vector{GNNGraph{Tuple{Vector{Int64}, Vector{Int64}, Nothing}}}:
GNNGraph(10, 20) with no data
GNNGraph(10, 14) with no data
GNNGraph(10, 22) with no data
julia> f(g) = g isa GNNGraph;
julia> f.(tg) # broadcast over snapshots
3-element BitVector:
1
1
1Basic Queries
Basic queries are similar to those for GNNGraphs:
julia> snapshots = [rand_graph(10,20), rand_graph(12,14), rand_graph(14,22)];
julia> tg = TemporalSnapshotsGNNGraph(snapshots)
TemporalSnapshotsGNNGraph:
num_nodes: [10, 12, 14]
num_edges: [20, 14, 22]
num_snapshots: 3
julia> tg.num_nodes # number of nodes in each snapshot
3-element Vector{Int64}:
10
12
14
julia> tg.num_edges # number of edges in each snapshot
3-element Vector{Int64}:
20
14
22
julia> tg.num_snapshots # number of snapshots
3
julia> tg.snapshots # list of snapshots
3-element Vector{GNNGraph{Tuple{Vector{Int64}, Vector{Int64}, Nothing}}}:
GNNGraph(10, 20) with no data
GNNGraph(12, 14) with no data
GNNGraph(14, 22) with no data
julia> tg.snapshots[1] # first snapshot, same as tg[1]
GNNGraph:
num_nodes: 10
num_edges: 20Data Features
A temporal graph can store global feature for the entire time series in the tgdata field. Also, each snapshot can store node, edge, and graph features in the ndata, edata, and gdata fields, respectively.
julia> snapshots = [rand_graph(10, 20; ndata = rand(Float32, 3, 10)),
rand_graph(10, 14; ndata = rand(Float32, 4, 10)),
rand_graph(10, 22; ndata = rand(Float32, 5, 10))]; # node features at construction time
julia> tg = TemporalSnapshotsGNNGraph(snapshots);
julia> tg.tgdata.y = rand(Float32, 3, 1); # add global features after construction
julia> tg
TemporalSnapshotsGNNGraph:
num_nodes: [10, 10, 10]
num_edges: [20, 14, 22]
num_snapshots: 3
tgdata:
y = 3×1 Matrix{Float32}
julia> tg.ndata # vector of DataStore containing node features for each snapshot
3-element Vector{DataStore}:
DataStore(10) with 1 element:
x = 3×10 Matrix{Float32}
DataStore(10) with 1 element:
x = 4×10 Matrix{Float32}
DataStore(10) with 1 element:
x = 5×10 Matrix{Float32}
julia> [ds.x for ds in tg.ndata]; # vector containing the x feature of each snapshot
julia> [g.x for g in tg.snapshots]; # same vector as above, now accessing
# the x feature directly from the snapshots