Skip to content
This repository was archived by the owner on Jun 18, 2026. It is now read-only.

Commit 3781147

Browse files
refactor: deduplicate buildAdjacency + BFS into shared GraphUtils
- Added GraphUtils.bfsDistancesFromAdj() for BFS on pre-built adjacency maps, complementing the existing bfsDistances() which takes a Graph. - GraphAutoPilot: removed private buildAdjacency() (~12 lines) and bfs() (~16 lines), replaced 6 call sites with GraphUtils methods. - GraphInformationDiffusionEngine: removed private buildAdjacency() (~13 lines) and bfs() (~15 lines), replaced 5 call sites. - GraphSentinel: removed private buildAdjacencyMap() (~5 lines), replaced 2 call sites with GraphUtils.buildAdjacencyMap(). Net: ~77 lines of duplicated adjacency/BFS code removed across 3 engines, consolidated into the existing GraphUtils utility class. All changed files compile cleanly against JUNG 2.0.1.
1 parent ec7fcba commit 3781147

4 files changed

Lines changed: 48 additions & 77 deletions

File tree

Gvisual/src/gvisual/GraphAutoPilot.java

Lines changed: 6 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ public void analyze() {
232232
return;
233233
}
234234

235-
adjacency = buildAdjacency(graph);
235+
adjacency = GraphUtils.buildAdjacencyMap(graph);
236236
beforeHealth = computeHealth(graph);
237237

238238
// Phase 1: Diagnose
@@ -710,7 +710,7 @@ private void planShortcuts() {
710710

711711
for (int t = 0; t < trials && count < 2; t++) {
712712
String start = vertices.get(t);
713-
Map<String, Integer> dist = bfs(start, adjacency);
713+
Map<String, Integer> dist = GraphUtils.bfsDistancesFromAdj(start, adjacency);
714714
// Find the farthest node
715715
String farthest = null;
716716
int maxDist = 0;
@@ -775,7 +775,7 @@ private HealthSnapshot computeHealth(Graph<String, Edge> g) {
775775
return h;
776776
}
777777

778-
Map<String, Set<String>> adj = buildAdjacency(g);
778+
Map<String, Set<String>> adj = GraphUtils.buildAdjacencyMap(g);
779779

780780
// Bridges
781781
h.bridgeCount = findBridges(g, adj).size();
@@ -823,19 +823,7 @@ private HealthSnapshot computeHealth(Graph<String, Edge> g) {
823823

824824
// ── Graph algorithms ───────────────────────────────────
825825

826-
private Map<String, Set<String>> buildAdjacency(Graph<String, Edge> g) {
827-
Map<String, Set<String>> adj = new HashMap<>();
828-
for (String v : g.getVertices()) adj.put(v, new HashSet<>());
829-
for (Edge e : g.getEdges()) {
830-
Collection<String> ep = g.getEndpoints(e);
831-
if (ep == null || ep.size() < 2) continue;
832-
Iterator<String> it = ep.iterator();
833-
String a = it.next(), b = it.next();
834-
adj.get(a).add(b);
835-
adj.get(b).add(a);
836-
}
837-
return adj;
838-
}
826+
// buildAdjacency removed — use GraphUtils.buildAdjacencyMap(graph)
839827

840828
/** Find bridge edges. Returns set of "u||v" keys (sorted). */
841829
private Set<String> findBridges(Graph<String, Edge> g, Map<String, Set<String>> adj) {
@@ -986,24 +974,7 @@ private List<Set<String>> findComponents(Graph<String, Edge> g, Map<String, Set<
986974
return components;
987975
}
988976

989-
/** BFS distances from a source. */
990-
private Map<String, Integer> bfs(String source, Map<String, Set<String>> adj) {
991-
Map<String, Integer> dist = new HashMap<>();
992-
Queue<String> queue = new ArrayDeque<>();
993-
dist.put(source, 0);
994-
queue.add(source);
995-
while (!queue.isEmpty()) {
996-
String u = queue.poll();
997-
int d = dist.get(u);
998-
for (String v : adj.getOrDefault(u, Collections.emptySet())) {
999-
if (!dist.containsKey(v)) {
1000-
dist.put(v, d + 1);
1001-
queue.add(v);
1002-
}
1003-
}
1004-
}
1005-
return dist;
1006-
}
977+
// bfs removed — use GraphUtils.bfsDistancesFromAdj(source, adj)
1007978

1008979
/** Approximate diameter using BFS from a few nodes. */
1009980
private double approximateDiameter(Graph<String, Edge> g, Map<String, Set<String>> adj) {
@@ -1012,7 +983,7 @@ private double approximateDiameter(Graph<String, Edge> g, Map<String, Set<String
1012983
int maxDist = 0;
1013984
int samples = Math.min(10, vertices.size());
1014985
for (int i = 0; i < samples; i++) {
1015-
Map<String, Integer> dist = bfs(vertices.get(i), adj);
986+
Map<String, Integer> dist = GraphUtils.bfsDistancesFromAdj(vertices.get(i), adj);
1016987
for (int d : dist.values()) {
1017988
if (d > maxDist) maxDist = d;
1018989
}

Gvisual/src/gvisual/GraphInformationDiffusionEngine.java

Lines changed: 5 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ public DiffusionReport analyze(Graph<String, Edge> graph) {
164164
}
165165

166166
// Build adjacency
167-
Map<String, Set<String>> adj = buildAdjacency(graph, vertices);
167+
Map<String, Set<String>> adj = GraphUtils.buildAdjacencyMap(graph);
168168

169169
// Select seeds
170170
Set<String> seeds = resolveSeedNodes(vertices, adj);
@@ -697,31 +697,15 @@ private int estimateDiameter(List<String> vertices, Map<String, Set<String>> adj
697697
List<String> probeNodes = new ArrayList<>(vertices);
698698
Collections.shuffle(probeNodes, rng);
699699
for (int p = 0; p < probes; p++) {
700-
Map<String, Integer> dist = bfs(probeNodes.get(p), adj);
700+
Map<String, Integer> dist = GraphUtils.bfsDistancesFromAdj(probeNodes.get(p), adj);
701701
for (int d : dist.values()) {
702702
if (d > maxDist) maxDist = d;
703703
}
704704
}
705705
return maxDist;
706706
}
707707

708-
private Map<String, Integer> bfs(String start, Map<String, Set<String>> adj) {
709-
Map<String, Integer> dist = new LinkedHashMap<>();
710-
dist.put(start, 0);
711-
Queue<String> queue = new LinkedList<>();
712-
queue.add(start);
713-
while (!queue.isEmpty()) {
714-
String u = queue.poll();
715-
int d = dist.get(u);
716-
for (String v : adj.getOrDefault(u, Collections.emptySet())) {
717-
if (!dist.containsKey(v)) {
718-
dist.put(v, d + 1);
719-
queue.add(v);
720-
}
721-
}
722-
}
723-
return dist;
724-
}
708+
// bfs removed — use GraphUtils.bfsDistancesFromAdj(source, adj)
725709

726710
private double computeGini(double[] sorted) {
727711
int n = sorted.length;
@@ -741,7 +725,7 @@ private double giantComponentFraction(List<String> vertices, Map<String, Set<Str
741725
int maxComp = 0;
742726
for (String v : vertices) {
743727
if (visited.contains(v)) continue;
744-
Map<String, Integer> comp = bfs(v, adj);
728+
Map<String, Integer> comp = GraphUtils.bfsDistancesFromAdj(v, adj);
745729
visited.addAll(comp.keySet());
746730
if (comp.size() > maxComp) maxComp = comp.size();
747731
}
@@ -815,19 +799,7 @@ private List<String> generateInsights(ICResult ic, LTResult lt,
815799
// Helpers
816800
// ==================================================================
817801

818-
private Map<String, Set<String>> buildAdjacency(Graph<String, Edge> graph, List<String> vertices) {
819-
Map<String, Set<String>> adj = new LinkedHashMap<>();
820-
for (String v : vertices) adj.put(v, new LinkedHashSet<>());
821-
for (Edge edge : graph.getEdges()) {
822-
String v1 = edge.getVertex1();
823-
String v2 = edge.getVertex2();
824-
if (v1 == null || v2 == null) continue;
825-
if (v1.equals(v2)) continue; // skip self-loops
826-
adj.computeIfAbsent(v1, k -> new LinkedHashSet<>()).add(v2);
827-
adj.computeIfAbsent(v2, k -> new LinkedHashSet<>()).add(v1);
828-
}
829-
return adj;
830-
}
802+
// buildAdjacency removed — use GraphUtils.buildAdjacencyMap(graph)
831803

832804
private Set<String> resolveSeedNodes(List<String> vertices, Map<String, Set<String>> adj) {
833805
if (seedNodes != null && !seedNodes.isEmpty()) {

Gvisual/src/gvisual/GraphSentinel.java

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -163,8 +163,8 @@ public DriftReport analyze() {
163163
report.edgesBefore = before.getEdgeCount();
164164
report.edgesAfter = after.getEdgeCount();
165165

166-
Map<String, Set<String>> adjBefore = buildAdjacencyMap(before);
167-
Map<String, Set<String>> adjAfter = buildAdjacencyMap(after);
166+
Map<String, Set<String>> adjBefore = GraphUtils.buildAdjacencyMap(before);
167+
Map<String, Set<String>> adjAfter = GraphUtils.buildAdjacencyMap(after);
168168

169169
// Pre-compute degrees and hubs once — reused by hub dynamics and stability scoring
170170
Map<String, Integer> degBefore = computeDegrees(before);
@@ -742,13 +742,7 @@ private List<Alert> generateAlerts(DriftReport report) {
742742

743743
// -- Utility methods ---------------------------------------------------
744744

745-
private Map<String, Set<String>> buildAdjacencyMap(Graph<String, Edge> g) {
746-
Map<String, Set<String>> adj = new HashMap<>();
747-
for (String v : g.getVertices()) {
748-
adj.put(v, new HashSet<>(g.getNeighbors(v)));
749-
}
750-
return adj;
751-
}
745+
// buildAdjacencyMap removed — use GraphUtils.buildAdjacencyMap(graph)
752746

753747
private Map<String, Integer> computeDegrees(Graph<String, Edge> g) {
754748
Map<String, Integer> deg = new HashMap<>();

Gvisual/src/gvisual/GraphUtils.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,40 @@ public static int cycleRankOfSubgraph(
406406
return edges - vertices.size() + comps;
407407
}
408408

409+
// ── BFS from adjacency map ─────────────────────────────────────────
410+
411+
/**
412+
* BFS from a source vertex using a pre-built adjacency map, returning
413+
* distances (hop counts) to all reachable vertices.
414+
*
415+
* <p>This variant is useful when the caller already holds an adjacency
416+
* map and wants to avoid re-traversing the JUNG graph structure.</p>
417+
*
418+
* @param source the starting vertex
419+
* @param adj pre-built adjacency map (vertex → set of neighbors)
420+
* @return map from vertex ID to its BFS distance from source
421+
*/
422+
public static Map<String, Integer> bfsDistancesFromAdj(
423+
String source, Map<String, Set<String>> adj) {
424+
Map<String, Integer> dist = new HashMap<String, Integer>();
425+
ArrayDeque<String> queue = new ArrayDeque<String>();
426+
dist.put(source, 0);
427+
queue.add(source);
428+
while (!queue.isEmpty()) {
429+
String u = queue.poll();
430+
int d = dist.get(u);
431+
Set<String> neighbors = adj.get(u);
432+
if (neighbors == null) continue;
433+
for (String v : neighbors) {
434+
if (!dist.containsKey(v)) {
435+
dist.put(v, d + 1);
436+
queue.add(v);
437+
}
438+
}
439+
}
440+
return dist;
441+
}
442+
409443
// ── Clustering coefficient ────────────────────────────────────────
410444

411445
/**

0 commit comments

Comments
 (0)