This repository was archived by the owner on Jun 18, 2026. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathTemporalGraph.java
More file actions
168 lines (155 loc) · 5.9 KB
/
Copy pathTemporalGraph.java
File metadata and controls
168 lines (155 loc) · 5.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
package gvisual;
import edu.uci.ics.jung.graph.Graph;
import edu.uci.ics.jung.graph.UndirectedSparseGraph;
import java.util.*;
/**
* A lightweight wrapper around a JUNG graph that provides time-windowed views
* of the network. This enables temporal analysis without changing any existing
* analyzer — analyzers receive a normal {@code Graph<String, edge>} for a
* specific time window.
*
* <p>Supports three modes of temporal access:</p>
* <ul>
* <li>{@link #snapshotAt(long)} — graph state at a single point in time</li>
* <li>{@link #windowBetween(long, long)} — graph state over a time range</li>
* <li>{@link #getTimePoints()} — all distinct timestamps in the graph</li>
* </ul>
*
* <p>All generated subgraphs are independent copies (new vertices and edges
* referencing the same edge objects) so modifications won't affect the
* original graph.</p>
*
* @author zalenix
*/
public class TemporalGraph {
private final Graph<String, edge> fullGraph;
/**
* Creates a TemporalGraph wrapping an existing JUNG graph.
*
* @param graph the full graph containing edges with optional timestamps
* @throws IllegalArgumentException if graph is null
*/
public TemporalGraph(Graph<String, edge> graph) {
if (graph == null) {
throw new IllegalArgumentException("Graph must not be null");
}
this.fullGraph = graph;
}
/**
* Returns the underlying full graph (all edges, all time periods).
*
* @return the full graph
*/
public Graph<String, edge> getFullGraph() {
return fullGraph;
}
/**
* Returns a snapshot of the graph at a specific point in time.
* Only edges that are active at the given time (per {@link edge#isActiveAt(long)})
* are included. Vertices with no active edges are excluded.
*
* @param time the point in time (epoch millis)
* @return a new graph containing only edges active at {@code time}
*/
public Graph<String, edge> snapshotAt(long time) {
Graph<String, edge> snapshot = new UndirectedSparseGraph<>();
for (edge e : fullGraph.getEdges()) {
if (e.isActiveAt(time)) {
addEdgeToGraph(snapshot, e);
}
}
return snapshot;
}
/**
* Returns a subgraph containing all edges active during any part of the
* given time window [start, end]. Vertices with no active edges are excluded.
*
* @param start window start (epoch millis, inclusive)
* @param end window end (epoch millis, inclusive)
* @return a new graph containing only edges active during the window
* @throws IllegalArgumentException if start > end
*/
public Graph<String, edge> windowBetween(long start, long end) {
if (start > end) {
throw new IllegalArgumentException(
"Start time must not be after end time: " + start + " > " + end);
}
Graph<String, edge> window = new UndirectedSparseGraph<>();
for (edge e : fullGraph.getEdges()) {
if (e.isActiveDuring(start, end)) {
addEdgeToGraph(window, e);
}
}
return window;
}
/**
* Returns all distinct timestamps present on edges in the graph,
* sorted in ascending order. Untimed edges (null timestamp) are excluded.
*
* @return sorted list of distinct epoch-millis timestamps
*/
public List<Long> getTimePoints() {
TreeSet<Long> times = new TreeSet<>();
for (edge e : fullGraph.getEdges()) {
if (e.getTimestamp() != null) {
times.add(e.getTimestamp());
}
if (e.getEndTimestamp() != null) {
times.add(e.getEndTimestamp());
}
}
return new ArrayList<>(times);
}
/**
* Returns the number of distinct time points in the graph.
*
* @return count of distinct timestamps
*/
public int getTimePointCount() {
return getTimePoints().size();
}
/**
* Generates a series of graph snapshots by dividing the full time range
* into equal-width windows. Useful for tracking network evolution over
* uniform time periods.
*
* @param windowCount number of windows to divide the time range into
* @return ordered list of (windowStart, graph) pairs
* @throws IllegalArgumentException if windowCount < 1
* @throws IllegalStateException if the graph has no timestamped edges
*/
public List<Map.Entry<Long, Graph<String, edge>>> generateWindows(int windowCount) {
if (windowCount < 1) {
throw new IllegalArgumentException("windowCount must be at least 1");
}
List<Long> times = getTimePoints();
if (times.isEmpty()) {
throw new IllegalStateException(
"Cannot generate windows: graph has no timestamped edges");
}
long minTime = times.get(0);
long maxTime = times.get(times.size() - 1);
long windowWidth = Math.max(1, (maxTime - minTime + 1) / windowCount);
List<Map.Entry<Long, Graph<String, edge>>> windows = new ArrayList<>();
for (int i = 0; i < windowCount; i++) {
long wStart = minTime + (i * windowWidth);
long wEnd = (i == windowCount - 1) ? maxTime : wStart + windowWidth - 1;
windows.add(new AbstractMap.SimpleImmutableEntry<>(wStart,
windowBetween(wStart, wEnd)));
}
return windows;
}
/**
* Adds an edge and its endpoints to a graph, skipping if the edge
* or vertices already exist.
*/
private void addEdgeToGraph(Graph<String, edge> graph, edge e) {
String v1 = e.getVertex1();
String v2 = e.getVertex2();
if (!graph.containsVertex(v1)) graph.addVertex(v1);
if (!graph.containsVertex(v2)) graph.addVertex(v2);
if (!graph.containsEdge(e)) {
graph.addEdge(e, v1, v2);
}
}
}