net.sf.gluebooster.java.booster.basic.math.graph.GraphSequenceClusterer Maven / Gradle / Ivy
package net.sf.gluebooster.java.booster.basic.math.graph;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.commons.collections4.Transformer;
import edu.uci.ics.jung.graph.Graph;
import edu.uci.ics.jung.graph.UndirectedGraph;
import net.sf.gluebooster.java.booster.essentials.eventsCommands.Callable;
import net.sf.gluebooster.java.booster.essentials.eventsCommands.CallableAbstraction;
import net.sf.gluebooster.java.booster.essentials.meta.objects.DisplayConfiguration;
/**
* Finds sequences within graphs.
*
* A sequence is a subgraph (with at least two vertices) with only at most two
* vertices that are connected with the rest of the graph. One of the vertices
* must have only at most one edge to the rest of the graph.
*
* A sequence is for example A-B-C-D
*
* @author CBauer
*
* @param
* @param
*/
public class GraphSequenceClusterer extends CallableAbstraction, Set>> implements
Transformer, Set>> {
/**
* Should the debug mode with more logging be enabled.
*/
private boolean debugMode = false;
/**
* @return the sequences
*/
@Override
public Set> transform(UndirectedGraph graph) {
if (debugMode) {
JungGraphBoostUtils.displayShapes(new DisplayConfiguration(
"graph to transform", (Graph) graph));
}
HashSet> result = new HashSet>();
Set usedVertices = new HashSet();
for (V vertex : graph.getVertices()) {
int degree = graph.degree(vertex);
if ((!usedVertices.contains(vertex)) && degree <= 2) {
// startpoint of the sequence
Set sequence = new HashSet();
if (degree == 0) {
// nothing to do
// end
} else {
continueSequence(sequence, usedVertices, graph, vertex,
false);
// the sequence size may be 1 if all other vertices are
// alredy used.
// ignore such sequences
if (sequence.size() > 1) {
result.add(sequence);
}
}
}
}
return result;
}
/**
* Continues finding the remainder of the sequence.
*
* @param sequence
* the result sequence
* @param usedVertices
* the vertices already inspected
* @param graph
* the graph which is to be inspected
* @param nextVertexOfSequence
* the next vertex that is to be inspected
* @param alreadyOneNodeWithMoreThan2Neighbours
* is there already one node with more than 2 neighbours.
*/
private void continueSequence(Set sequence, Set usedVertices,
UndirectedGraph graph, V nextVertexOfSequence,
boolean alreadyOneNodeWithMoreThan2Neighbours) {
if (!usedVertices.contains(nextVertexOfSequence)) {
Collection neighbours = graph.getNeighbors(nextVertexOfSequence);
int neighboursSize = neighbours.size();
// a node of the sequence either has max 2 neighbours, or it is an
// end of the sequence
// one end is allowed in the sequence
if ((neighboursSize <= 2 || !alreadyOneNodeWithMoreThan2Neighbours)) {
HashSet usedNeighbours = new HashSet(neighbours);
usedNeighbours.retainAll(usedVertices);
// the nodes of the sequence, that are already used
usedVertices.add(nextVertexOfSequence);
if (usedNeighbours.size() > 1) {
// there is a circle, don't use that node
return;
}
sequence.add(nextVertexOfSequence);
if (neighboursSize > 2) {
alreadyOneNodeWithMoreThan2Neighbours = true;
} else {
for (V neighbour : neighbours) {
continueSequence(sequence, usedVertices, graph,
neighbour,
alreadyOneNodeWithMoreThan2Neighbours);
}
}
}
}
}
/**
* Orders a sequence.
*
* @param sequence
* the elements must form a sequence a-b-c-d-e...
* @param graph
* the inspected graph
* @param hashCodeOfVerticesMayHaveChanged
* if the hash code has changed some more work is to do.
* @return the ordered sequence
*/
public List order(Collection sequence, Graph graph,
boolean hashCodeOfVerticesMayHaveChanged) {
if (graph == null) {
throw new IllegalStateException("graph must not be null");
}
if (hashCodeOfVerticesMayHaveChanged) {
// create a new graph with working hash codes
graph = JungGraphBoostUtils.createUndirectedGraph(graph);
}
ArrayList result = new ArrayList(sequence.size());
// the nodes that have not yet been added to the result
HashSet unused = new HashSet(sequence);
if (!sequence.isEmpty()) {
V current = unused.iterator().next();
// successors
while (current != null) {
result.add(current);
unused.remove(current);
Collection neighbours = graph.getNeighbors(current);
if (neighbours == null) {
throw new IllegalStateException(
"current node not found in graph. Maybe the hash code of the node changed");
}
current = null;
for (V neighbour : neighbours) {
if (unused.contains(neighbour)) {
// continue with the neighbour
current = neighbour;
break;
}
}
}
// predecessors
current = result.get(0);
while (current != null) {
// do not in set the current here, because it is alredy in the
// list
Collection neighbours = graph.getNeighbors(current);
current = null;
for (V neighbour : neighbours) {
if (unused.contains(neighbour)) {
result.add(0, neighbour);
unused.remove(neighbour);
// continue with the neighbour
current = neighbour;
break;
}
}
}
}
return result;
}
public boolean isDebugMode() {
return debugMode;
}
public void setDebugMode(boolean debugMode) {
this.debugMode = debugMode;
}
@Override
protected Set> callImpl(UndirectedGraph... parameters) throws Exception {
return transform(parameters[0]);
}
}