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

Commit e1b857e

Browse files
refactor: deduplicate clustering coefficient + BFS into GraphUtils
Extract clusteringCoefficient() and avgClusteringCoefficient() into GraphUtils, replacing 5 independent inline implementations across CsvReportExporter, GraphStatsDashboard, GraphStorytellerExporter, GrowthRateAnalyzer. Also replace inline BFS in GraphDrawingQualityAnalyzer with GraphUtils.bfsDistances, and inline component counting in GraphStatsDashboard with GraphUtils.findComponents. Removes ~60 lines of duplicated graph traversal code.
1 parent 4afd350 commit e1b857e

6 files changed

Lines changed: 50 additions & 109 deletions

File tree

Gvisual/src/gvisual/CsvReportExporter.java

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -257,22 +257,7 @@ private Map<String, Integer> computeCommunities() {
257257
private Map<String, Double> computeClusteringCoefficients() {
258258
Map<String, Double> result = new LinkedHashMap<String, Double>();
259259
for (String v : graph.getVertices()) {
260-
Collection<String> neighbors = graph.getNeighbors(v);
261-
List<String> nList = new ArrayList<String>(neighbors);
262-
int k = nList.size();
263-
if (k < 2) {
264-
result.put(v, 0.0);
265-
continue;
266-
}
267-
int triangles = 0;
268-
for (int i = 0; i < k; i++) {
269-
for (int j = i + 1; j < k; j++) {
270-
if (graph.isNeighbor(nList.get(i), nList.get(j))) {
271-
triangles++;
272-
}
273-
}
274-
}
275-
result.put(v, (2.0 * triangles) / (k * (k - 1)));
260+
result.put(v, GraphUtils.clusteringCoefficient(graph, v));
276261
}
277262
return result;
278263
}

Gvisual/src/gvisual/GraphDrawingQualityAnalyzer.java

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -498,28 +498,10 @@ private void ensureDistCache(List<String> verts) {
498498
if (distCache != null) return;
499499
distCache = new HashMap<>();
500500
for (String s : verts) {
501-
distCache.put(s, bfs(s));
501+
distCache.put(s, GraphUtils.bfsDistances(graph, s));
502502
}
503503
}
504504

505-
private Map<String, Integer> bfs(String source) {
506-
Map<String, Integer> dist = new HashMap<>();
507-
dist.put(source, 0);
508-
Queue<String> queue = new ArrayDeque<>();
509-
queue.add(source);
510-
while (!queue.isEmpty()) {
511-
String u = queue.poll();
512-
int d = dist.get(u);
513-
for (String nbr : graph.getNeighbors(u)) {
514-
if (!dist.containsKey(nbr)) {
515-
dist.put(nbr, d + 1);
516-
queue.add(nbr);
517-
}
518-
}
519-
}
520-
return dist;
521-
}
522-
523505
private static double convexHullArea(List<Point2D> points) {
524506
if (points.size() < 3) return 0;
525507

Gvisual/src/gvisual/GraphStatsDashboard.java

Lines changed: 3 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -84,18 +84,7 @@ private List<Map.Entry<String, Integer>> topVerticesByDegree(int n) {
8484
}
8585

8686
private double clusteringCoefficient(String v) {
87-
Collection<String> neighbors = graph.getNeighbors(v);
88-
if (neighbors == null) return 0;
89-
List<String> nList = new ArrayList<>(neighbors);
90-
int k = nList.size();
91-
if (k < 2) return 0;
92-
int links = 0;
93-
for (int i = 0; i < k; i++) {
94-
for (int j = i + 1; j < k; j++) {
95-
if (graph.isNeighbor(nList.get(i), nList.get(j))) links++;
96-
}
97-
}
98-
return (2.0 * links) / (k * (k - 1));
87+
return GraphUtils.clusteringCoefficient(graph, v);
9988
}
10089

10190
private Map<String, Integer> edgeTypeBreakdown() {
@@ -121,30 +110,11 @@ private double avgDegree() {
121110
}
122111

123112
private double avgClusteringCoefficient() {
124-
if (graph.getVertexCount() == 0) return 0;
125-
double sum = 0;
126-
for (String v : graph.getVertices()) sum += clusteringCoefficient(v);
127-
return sum / graph.getVertexCount();
113+
return GraphUtils.avgClusteringCoefficient(graph);
128114
}
129115

130116
private int connectedComponents() {
131-
Set<String> visited = new HashSet<>();
132-
int count = 0;
133-
for (String v : graph.getVertices()) {
134-
if (!visited.contains(v)) {
135-
count++;
136-
Queue<String> queue = new ArrayDeque<>();
137-
queue.add(v);
138-
visited.add(v);
139-
while (!queue.isEmpty()) {
140-
String cur = queue.poll();
141-
for (String nb : graph.getNeighbors(cur)) {
142-
if (visited.add(nb)) queue.add(nb);
143-
}
144-
}
145-
}
146-
}
147-
return count;
117+
return GraphUtils.findComponents(graph).size();
148118
}
149119

150120
// ── clustering coefficient histogram bins ────────────────────────

Gvisual/src/gvisual/GraphStorytellerExporter.java

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -272,23 +272,7 @@ private List<Set<String>> findComponents() {
272272
}
273273

274274
private double computeAvgClustering() {
275-
double total = 0;
276-
int count = 0;
277-
for (String v : graph.getVertices()) {
278-
Collection<String> neighbors = graph.getNeighbors(v);
279-
int k = neighbors.size();
280-
if (k < 2) continue;
281-
List<String> nbList = new ArrayList<>(neighbors);
282-
int links = 0;
283-
for (int i = 0; i < nbList.size(); i++) {
284-
for (int j = i + 1; j < nbList.size(); j++) {
285-
if (graph.isNeighbor(nbList.get(i), nbList.get(j))) links++;
286-
}
287-
}
288-
total += (2.0 * links) / (k * (k - 1));
289-
count++;
290-
}
291-
return count > 0 ? total / count : 0;
275+
return GraphUtils.avgClusteringCoefficient(graph);
292276
}
293277

294278
private long countTriangles() {

Gvisual/src/gvisual/GraphUtils.java

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,49 @@ public static int cycleRankOfSubgraph(
333333
return edges - vertices.size() + comps;
334334
}
335335

336+
// ── Clustering coefficient ────────────────────────────────────────
337+
338+
/**
339+
* Computes the local clustering coefficient for a single vertex.
340+
* <p>C(v) = 2 * triangles(v) / (deg(v) * (deg(v) - 1))</p>
341+
*
342+
* @param graph the JUNG graph
343+
* @param v the vertex
344+
* @return clustering coefficient in [0, 1], or 0 if degree &lt; 2
345+
*/
346+
public static double clusteringCoefficient(
347+
Graph<String, Edge> graph, String v) {
348+
Collection<String> neighbors = graph.getNeighbors(v);
349+
if (neighbors == null) return 0.0;
350+
List<String> nList = new ArrayList<String>(neighbors);
351+
int k = nList.size();
352+
if (k < 2) return 0.0;
353+
int links = 0;
354+
for (int i = 0; i < k; i++) {
355+
for (int j = i + 1; j < k; j++) {
356+
if (graph.isNeighbor(nList.get(i), nList.get(j))) links++;
357+
}
358+
}
359+
return (2.0 * links) / (k * (k - 1));
360+
}
361+
362+
/**
363+
* Computes the average clustering coefficient across all vertices.
364+
*
365+
* @param graph the JUNG graph
366+
* @return average clustering coefficient, or 0 if the graph is empty
367+
*/
368+
public static double avgClusteringCoefficient(
369+
Graph<String, Edge> graph) {
370+
int n = graph.getVertexCount();
371+
if (n == 0) return 0.0;
372+
double sum = 0.0;
373+
for (String v : graph.getVertices()) {
374+
sum += clusteringCoefficient(graph, v);
375+
}
376+
return sum / n;
377+
}
378+
336379
// ── Graph copy ──────────────────────────────────────────────────────
337380

338381
/**

Gvisual/src/gvisual/GrowthRateAnalyzer.java

Lines changed: 1 addition & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -116,29 +116,6 @@ private static double computeDensity(int nodes, int edges) {
116116
}
117117

118118
private static double computeAvgClustering(Graph<String, Edge> g) {
119-
if (g.getVertexCount() == 0) return 0.0;
120-
121-
Map<String, Set<String>> adj = GraphUtils.buildAdjacencyMap(g);
122-
double totalClustering = 0.0;
123-
for (String v : g.getVertices()) {
124-
Set<String> neighbors = adj.get(v);
125-
if (neighbors == null) continue;
126-
List<String> neighborList = new ArrayList<>(neighbors);
127-
int k = neighborList.size();
128-
if (k < 2) continue;
129-
130-
int triangles = 0;
131-
for (int i = 0; i < k; i++) {
132-
Set<String> ni = adj.get(neighborList.get(i));
133-
for (int j = i + 1; j < k; j++) {
134-
if (ni != null && ni.contains(neighborList.get(j))) {
135-
triangles++;
136-
}
137-
}
138-
}
139-
double maxTriangles = (double) k * (k - 1) / 2.0;
140-
totalClustering += triangles / maxTriangles;
141-
}
142-
return totalClustering / g.getVertexCount();
119+
return GraphUtils.avgClusteringCoefficient(g);
143120
}
144121
}

0 commit comments

Comments
 (0)