Skip to content
This repository was archived by the owner on Jun 18, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions Gvisual/src/gvisual/CycleAnalyzer.java
Original file line number Diff line number Diff line change
Expand Up @@ -661,8 +661,7 @@ private Iterable<String> getSuccessors(String v) {
}

private Iterable<String> getNeighbors(String v) {
Collection<String> nbrs = graph.getNeighbors(v);
return nbrs != null ? nbrs : Collections.<String>emptyList();
return GraphUtils.neighborsOf(graph, v);
}

private String edgeKey(String v1, String v2) {
Expand Down
33 changes: 13 additions & 20 deletions Gvisual/src/gvisual/GraphColoringAnalyzer.java
Original file line number Diff line number Diff line change
Expand Up @@ -180,13 +180,11 @@ public ColoringResult computeDSatur() {

// Assign smallest available color
Set<Integer> usedColors = new HashSet<>();
Collection<String> neighbors = graph.getNeighbors(best);
if (neighbors != null) {
for (String neighbor : neighbors) {
Integer nc = colorAssignment.get(neighbor);
if (nc != null) {
usedColors.add(nc);
}
Collection<String> neighbors = GraphUtils.neighborsOf(graph, best);
for (String neighbor : neighbors) {
Integer nc = colorAssignment.get(neighbor);
if (nc != null) {
usedColors.add(nc);
}
}

Expand Down Expand Up @@ -242,10 +240,8 @@ public int chromaticLowerBound() {

// Sort candidates by degree descending for better heuristic
List<String> candidates = new ArrayList<>();
Collection<String> neighbors = graph.getNeighbors(start);
if (neighbors != null) {
candidates.addAll(neighbors);
}
Collection<String> neighbors = GraphUtils.neighborsOf(graph, start);
candidates.addAll(neighbors);
candidates.sort((a, b) -> Integer.compare(graph.degree(b), graph.degree(a)));

for (String candidate : candidates) {
Expand Down Expand Up @@ -359,8 +355,7 @@ private boolean backtrackColor(List<String> vertices, int idx, int k,

private boolean canAssign(String vertex, int color,
Map<String, Integer> assignment) {
Collection<String> neighbors = graph.getNeighbors(vertex);
if (neighbors == null) return true;
Collection<String> neighbors = GraphUtils.neighborsOf(graph, vertex);
for (String neighbor : neighbors) {
Integer nc = assignment.get(neighbor);
if (nc != null && nc == color) {
Expand Down Expand Up @@ -637,13 +632,11 @@ private ColoringResult greedyColor(List<String> vertexOrder) {

for (String vertex : vertexOrder) {
Set<Integer> usedColors = new HashSet<>();
Collection<String> neighbors = graph.getNeighbors(vertex);
if (neighbors != null) {
for (String neighbor : neighbors) {
Integer neighborColor = colorAssignment.get(neighbor);
if (neighborColor != null) {
usedColors.add(neighborColor);
}
Collection<String> neighbors = GraphUtils.neighborsOf(graph, vertex);
for (String neighbor : neighbors) {
Integer neighborColor = colorAssignment.get(neighbor);
if (neighborColor != null) {
usedColors.add(neighborColor);
}
}

Expand Down
7 changes: 3 additions & 4 deletions Gvisual/src/gvisual/GraphEntropyAnalyzer.java
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ private void computeNeighbourhoodEntropy() {

double sum = 0;
for (String v : graph.getVertices()) {
Collection<String> nbrs = graph.getNeighbors(v);
Collection<String> nbrs = GraphUtils.neighborsOf(graph, v);
if (nbrs == null || nbrs.isEmpty()) {
neighbourhoodEntropy.put(v, 0.0);
continue;
Expand Down Expand Up @@ -323,7 +323,7 @@ private void computeChromaticEntropy() {
Map<String, Integer> colors = new HashMap<>();
for (String v : vertices) {
Set<Integer> usedColors = new HashSet<>();
Collection<String> nbrs = graph.getNeighbors(v);
Collection<String> nbrs = GraphUtils.neighborsOf(graph, v);
if (nbrs != null) {
for (String u : nbrs) {
Integer c = colors.get(u);
Expand Down Expand Up @@ -452,8 +452,7 @@ private static double logFactorial(int n) {
* C(v) = 2T / (d(v)(d(v)-1)) where T is the number of triangles.
*/
private double localClusteringCoefficient(String v) {
Collection<String> nbrs = graph.getNeighbors(v);
if (nbrs == null) return 0;
Collection<String> nbrs = GraphUtils.neighborsOf(graph, v);
List<String> nbrList = new ArrayList<>(nbrs);
int d = nbrList.size();
if (d < 2) return 0;
Expand Down
72 changes: 72 additions & 0 deletions Gvisual/src/gvisual/GraphUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -648,4 +648,76 @@ public static List<String> reconstructPath(
Collections.reverse(path);
return path;
}

// ── Null-safe neighbor access ───────────────────────────────

/**
* Returns the neighbors of a vertex, never {@code null}.
* Wraps {@code graph.getNeighbors(v)} with a null-safe fallback.
*
* @param graph the JUNG graph
* @param v the vertex
* @return neighbors of v, or an empty collection if null
*/
public static Collection<String> neighborsOf(
Graph<String, edge> graph, String v) {
Collection<String> nbrs = graph.getNeighbors(v);
return nbrs != null ? nbrs : Collections.<String>emptyList();
}

// ── Directed adjacency ──────────────────────────────────────

/**
* Directed adjacency structure: vertices with successor and predecessor
* maps. Extracted from {@link TopologicalSortAnalyzer} for reuse by
* any analyzer that needs directed-edge traversal.
*/
public static final class DirectedAdj {
/** All vertices in the graph. */
public final Set<String> vertices;
/** Vertex → set of outgoing neighbors (vertex1 → vertex2). */
public final Map<String, Set<String>> successors;
/** Vertex → set of incoming neighbors. */
public final Map<String, Set<String>> predecessors;

public DirectedAdj(Set<String> vertices,
Map<String, Set<String>> successors,
Map<String, Set<String>> predecessors) {
this.vertices = vertices;
this.successors = successors;
this.predecessors = predecessors;
}
}

/**
* Builds directed adjacency maps from a graph. Each edge is interpreted
* as vertex1 → vertex2.
*
* @param graph the JUNG graph
* @return a {@link DirectedAdj} with successor and predecessor maps
*/
public static DirectedAdj buildDirectedAdjacencyMap(
Graph<String, edge> graph) {
Map<String, Set<String>> successors = new HashMap<String, Set<String>>();
Map<String, Set<String>> predecessors = new HashMap<String, Set<String>>();
Set<String> allVertices = new HashSet<String>();

for (String v : graph.getVertices()) {
allVertices.add(v);
successors.put(v, new HashSet<String>());
predecessors.put(v, new HashSet<String>());
}

for (edge e : graph.getEdges()) {
String from = e.getVertex1();
String to = e.getVertex2();
if (from != null && to != null
&& allVertices.contains(from) && allVertices.contains(to)) {
successors.get(from).add(to);
predecessors.get(to).add(from);
}
}

return new DirectedAdj(allVertices, successors, predecessors);
}
}
48 changes: 7 additions & 41 deletions Gvisual/src/gvisual/TopologicalSortAnalyzer.java
Original file line number Diff line number Diff line change
Expand Up @@ -183,46 +183,12 @@ public VertexDependencyInfo(String vertex, Set<String> allDependencies,
* {@link #analyze()}, {@link #analyzeDependencies(String)}, and
* {@link #countChoicePoints()}.
*/
private static class DirectedAdj {
final Set<String> vertices;
final Map<String, Set<String>> successors;
final Map<String, Set<String>> predecessors;

DirectedAdj(Set<String> vertices,
Map<String, Set<String>> successors,
Map<String, Set<String>> predecessors) {
this.vertices = vertices;
this.successors = successors;
this.predecessors = predecessors;
}
}

/**
* Builds the directed adjacency maps from the graph. Each edge is
* interpreted as vertex1 → vertex2 (vertex1 must come before vertex2).
* Delegates to {@link GraphUtils.DirectedAdj} — the shared directed
* adjacency builder extracted from this class.
*/
private DirectedAdj buildDirectedAdj() {
Map<String, Set<String>> successors = new HashMap<String, Set<String>>();
Map<String, Set<String>> predecessors = new HashMap<String, Set<String>>();
Set<String> allVertices = new HashSet<String>();

for (String v : graph.getVertices()) {
allVertices.add(v);
successors.put(v, new HashSet<String>());
predecessors.put(v, new HashSet<String>());
}

for (edge e : graph.getEdges()) {
String from = e.getVertex1();
String to = e.getVertex2();
if (from != null && to != null
&& allVertices.contains(from) && allVertices.contains(to)) {
successors.get(from).add(to);
predecessors.get(to).add(from);
}
}

return new DirectedAdj(allVertices, successors, predecessors);
private GraphUtils.DirectedAdj buildDirectedAdj() {
return GraphUtils.buildDirectedAdjacencyMap(graph);
}

// ── Core algorithms ─────────────────────────────────────────
Expand All @@ -240,7 +206,7 @@ private DirectedAdj buildDirectedAdj() {
* @return complete topological sort analysis
*/
public TopologicalSortResult analyze() {
DirectedAdj adj = buildDirectedAdj();
GraphUtils.DirectedAdj adj = buildDirectedAdj();
Map<String, Set<String>> successors = adj.successors;
Map<String, Set<String>> predecessors = adj.predecessors;
Set<String> allVertices = adj.vertices;
Expand Down Expand Up @@ -351,7 +317,7 @@ public VertexDependencyInfo analyzeDependencies(String vertex) {
return null;
}

DirectedAdj adj = buildDirectedAdj();
GraphUtils.DirectedAdj adj = buildDirectedAdj();
Map<String, Set<String>> successors = adj.successors;
Map<String, Set<String>> predecessors = adj.predecessors;

Expand Down Expand Up @@ -416,7 +382,7 @@ public int countChoicePoints() {

// Re-use shared adjacency builder and count points where
// multiple vertices are ready simultaneously
DirectedAdj adj = buildDirectedAdj();
GraphUtils.DirectedAdj adj = buildDirectedAdj();
Map<String, Set<String>> successors = adj.successors;
Map<String, Integer> inDegree = new HashMap<String, Integer>();

Expand Down
Loading