Getting started
This page walks through a typical analysis: building a temporal graph, computing distances and paths, and ranking nodes by centrality.
Temporal edges
A TemporalEdge (u, v, t, tt) is a directed contact from node u to node v, starting at time t and taking tt time units, its transition time, to traverse. The edge leaves u at time t and reaches v at time t + tt. When transition times are not given, they default to 1.
julia> e = TemporalEdge(1, 2, 10) # tt = 1
(1 2 10 1)
julia> e.t + e.tt # arrival time
11Node ids are 1-based integers. By default they are stored as Int32, which keeps an edge with integer times at 24 bytes; the time type is inferred from t and tt and can be any real type, e.g. Float64 for continuous time:
julia> TemporalEdge(1, 2, 0.5, 0.25)
(1 2 0.5 0.25)
julia> typeof(ans)
TemporalEdge{Int32, Float64}Building a temporal graph
The main representation in this package is the OrderedEdgeList, a chronologically sorted edge stream. You can build it from a vector of tuples (u, v, t, tt) or of TemporalEdges, for nodes 1:n. The timelines of the graph built below, one per node:
julia> g = OrderedEdgeList(3, [(1, 2, 5, 1), (2, 3, 7, 1), (1, 3, 1, 3)])
OrderedEdgeList{Int32, Int64} with 3 nodes, 3 temporal edges, time interval (1, 8)
julia> edges(g) # sorted by (t, tt, u, v)
3-element Vector{TemporalEdge{Int32, Int64}}:
(1 3 1 3)
(1 2 5 1)
(2 3 7 1)The time interval defaults to (earliest departure, latest arrival); you can pass another one as the third argument.
Nodes over time
The nodes 1:n exist during the whole time interval: only the edges depend on time. There is no separate notion of a node joining or leaving the network. A node that has no edges at some time is isolated then: it is still a node of the graph, but it cannot reach or be reached by the others at that time.
Results over all nodes therefore include the inactive ones: a graph with a node that never has an edge is not temporally connected (is_temporally_connected), isolated nodes form components of size 1, and every snapshot (snapshots) has all n nodes. The loaders (load_ordered_edge_list, load_dataset) number only the nodes that appear in some edge. To study a subset of the nodes, for example those active in a time window, build a new graph from the edges between them.
From files
load_ordered_edge_list reads text files with one edge u v t [tt] per line, the format used by TGLib and by many public datasets (e.g. the SNAP temporal networks). Spaces, tabs and commas are accepted as separators and lines starting with # or % are skipped:
g = load_ordered_edge_list("CollegeMsg.txt") # integer times
g = load_ordered_edge_list("contacts.csv"; time_type=Float64) # real times
g = load_ordered_edge_list("contacts.txt"; directed=false) # both directionsThe node ids of the file can be arbitrary integers; they are renumbered to 1:n in order of first appearance, and original_id maps them back:
julia> path = tempname(); write(path, "100 7 1\n7 42 3\n");
julia> h = load_ordered_edge_list(path);
julia> edges(h)
2-element Vector{TemporalEdge{Int32, Int64}}:
(1 2 1 1)
(2 3 3 1)
julia> original_id(h, 3)
42save_ordered_edge_list writes a graph in the same format.
From tables
Any Tables.jl source, such as a DataFrame or a CSV.File, can be turned into a temporal graph by naming its columns:
julia> tbl = (from=[10, 20, 10], to=[20, 30, 30], time=[1.0, 2.5, 4.0]);
julia> OrderedEdgeList(tbl; u=:from, v=:to, t=:time)
OrderedEdgeList{Int32, Float64} with 3 nodes, 3 temporal edges, time interval (1.0, 5.0)Distances
From here on, the examples use the graph g of the quick example:
Temporal distances from a source node come in five flavors (see Paths and distances for the definitions). Each has its own function, and temporal_distances selects one with a DistanceType:
julia> earliest_arrival_times(g, 1)
4-element TemporalDistances{Int64}:
0
3
8
6
julia> minimum_hops(g, 1)
4-element TemporalDistances{Int64}:
0
1
2
1
julia> temporal_distances(g, 1, MinimumTransitionTimes())
4-element TemporalDistances{Int64}:
0
1
6
3Unreachable nodes get typemax of the element type (INF for Int64, Inf for Float64), printed as ∞; test them with d[v] == typemax(eltype(d)), or d[v] == INF for Int64 times:
julia> minimum_durations(g, 3)
4-element TemporalDistances{Int64}:
∞
1
0
3All distance and path functions accept a time window as last argument; only edges that depart and arrive inside the window are used:
julia> earliest_arrival_times(g, 1, (2, 9))
4-element TemporalDistances{Int64}:
0
3
∞
9Paths
The path functions return the edges of one optimal path (empty if the target cannot be reached):
julia> earliest_arrival_path(g, 1, 3)
2-element Vector{TemporalEdge{Int32, Int64}}:
(1 4 1 5)
(4 3 6 2)
julia> minimum_transition_time_path(g, 1, 4)
2-element Vector{TemporalEdge{Int32, Int64}}:
(1 2 2 1)
(2 4 7 2)Centralities
Closeness, eccentricity, diameter and efficiency take a distance type:
julia> temporal_closeness(g, Fastest())
4-element Vector{Float64}:
1.3928571428571428
0.5
1.3333333333333333
1.0
julia> compute_topk_closeness(g, 2, Fastest()) # (node, closeness) pairs
2-element Vector{Tuple{Int64, Float64}}:
(1, 1.3928571428571428)
(3, 1.3333333333333333)
julia> temporal_diameter(g, MinimumHops())
2Walk-based centralities work on the edge stream directly:
julia> temporal_katz_centrality(g, 1.0) # β = 1: walks ending at each node
4-element Vector{Float64}:
0.0
3.0
8.0
5.0
julia> temporal_walk_centrality(g, 1.0, 1.0) # walks passing through each node
4-element Vector{Float64}:
0.0
6.0
0.0
6.0get_statistics summarizes a graph, including the temporal graph parameters of Centralities and statistics:
julia> get_statistics(g)
number of nodes: 4
number of edges: 7
number of static edges: 5
number of time stamps: 6
number of transition times: 3
min. time stamp: 1
max. time stamp: 8
min. transition time: 1
max. transition time: 5
min. temporal in-degree: 0
max. temporal in-degree: 3
min. temporal out-degree: 1
max. temporal out-degree: 3
temporality: 2
max. edges per time stamp: 2
vertex-interval-membership width: 3
edge-interval-membership width: 2See Centralities and statistics for all measures.
Drawing
draw_graph draws the static graph, with every edge labeled by its (t, tt), and draw_timelines draws one timeline per node, with every edge going from u at time t to v at time t + tt:
using TemporalGraphs
g = OrderedEdgeList(4, [(1, 4, 1, 5), (1, 2, 2, 1), (1, 2, 5, 2), (3, 2, 6, 1),
(4, 3, 6, 2), (2, 4, 7, 2), (4, 3, 8, 1)])
draw_graph(g)draw_timelines(g; order=[1, 2, 4, 3]) # the timelines from top to bottomBoth return a TemporalGraphDrawing, shown as an image by notebooks, the VS Code plot pane and this documentation; write("graph.svg", draw_graph(g)) saves it. They draw every edge, so they are meant for small graphs.
Next steps
- Paths and distances: the definitions of the distance types, time windows and the refined optimality criteria.
- Centralities and statistics: closeness, betweenness, walk-based centralities, local statistics and temporal graph parameters.
- Connectivity, flows and spanners: reachability, temporal components, seed selection, restless walks, flows and spanners.
- Motifs, reference models and generators: motif counts, null models and random temporal graphs.
- Datasets and machine learning: public networks and the integration with MLDatasets.jl and GraphNeuralNetworks.jl.
- Graph representations: when to convert to incident lists or to the time-respecting static graph.
- Performance tips: in-place computations, multithreading and the choice of representation.