Commit 0b39dee1 authored by Roland Kluge's avatar Roland Kluge
Browse files

Extend Graph with data structures for storing inverse edge pairs

parent 8f0b330b
......@@ -32,6 +32,9 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import de.tudarmstadt.maki.simonstrator.api.Graphs;
/**
......@@ -65,6 +68,10 @@ public class BasicGraph implements Graph {
// Assigns to each node the set of predecessors of this node
private final Map<INodeID, HashSet<INodeID>> predecessorNodes = new HashMap<>();
// Maps an edge to its inverse edge. Each pair of edges should only be
// contained once (i.e., check both directions).
private BiMap<IEdge, IEdge> inverseEdgeMapping = HashBiMap.create();
/**
* Creates an empty graph. Components should create new graphs by using the
* {@link Graphs} class in the API
......@@ -83,8 +90,7 @@ public class BasicGraph implements Graph {
* Set of edges contained in the graph. Assumes that every start
* and end node in the edges is also contained in the nodes set
*/
public BasicGraph(Iterable<? extends INode> nodes,
Iterable<? extends IEdge> edges) {
public BasicGraph(Iterable<? extends INode> nodes, Iterable<? extends IEdge> edges) {
this();
for (final INode node : nodes)
......@@ -111,8 +117,7 @@ public class BasicGraph implements Graph {
this.addElements(edges);
}
public static BasicGraph createGraphFromNodeIdsAndEdges(
Iterable<? extends INodeID> nodes,
public static BasicGraph createGraphFromNodeIdsAndEdges(Iterable<? extends INodeID> nodes,
Iterable<? extends IEdge> edges) {
final BasicGraph graph = new BasicGraph();
for (INodeID nodeId : nodes) {
......@@ -190,11 +195,11 @@ public class BasicGraph implements Graph {
this.incomingAdjacencyList.get(edge.toId()).add(edge);
this.successorNodes.get(edge.fromId()).add(edge.toId());
this.predecessorNodes.get(edge.toId()).add(edge.fromId());
return true;
return true;
} else {
return false;
}
}
}
}
@Override
public boolean addNode(INode node) {
......@@ -205,11 +210,11 @@ public class BasicGraph implements Graph {
this.outgoingAdjacencyList.put(node.getId(), new HashSet<IEdge>());
this.successorNodes.put(node.getId(), new HashSet<INodeID>());
this.predecessorNodes.put(node.getId(), new HashSet<INodeID>());
return true;
return true;
} else {
return false;
}
}
}
@Override
public void createAndAddNode(INodeID nodeId) {
......@@ -231,14 +236,14 @@ public class BasicGraph implements Graph {
} else {
throw new AssertionError("Unknown graph element type.");
}
}
}
@Override
public boolean removeNode(INodeID node) {
if (this.containsNode(node))
return this.removeNode(this.getNode(node));
return false;
}
}
private boolean removeNode(INode node) {
if (!this.containsNode(node.getId()))
......@@ -257,8 +262,8 @@ public class BasicGraph implements Graph {
nodesById.remove(node.getId());
nodes.remove(node);
return true;
}
return true;
}
@Override
public boolean removeEdge(IEdge edge) {
......@@ -315,8 +320,7 @@ public class BasicGraph implements Graph {
else if (element instanceof IEdge)
return containsEdge((IEdge) element);
else
throw new IllegalArgumentException(
"Unsupported kind of element: " + element);
throw new IllegalArgumentException("Unsupported kind of element: " + element);
}
@Override
......@@ -378,6 +382,35 @@ public class BasicGraph implements Graph {
return this.edgesById.get(edgeID);
}
@Override
public IEdge getInverseEdge(EdgeID edgeId) {
final IEdge edge = getEdge(edgeId);
if (edge == null)
throw new IllegalArgumentException("Cannot retrieve inverse edge of " + edgeId + " because " + edgeId
+ " is not contained in this graph");
return getInverseEdge(edge);
}
@Override
public IEdge getInverseEdge(IEdge edge) {
final IEdge forwardLookup = inverseEdgeMapping.get(edge);
if (forwardLookup == null) {
return inverseEdgeMapping.inverse().get(edge);
}
return forwardLookup;
}
@Override
public void makeInverseEdges(EdgeID forwardEdgeId, EdgeID backwardEdgeId) {
this.makeInverseEdges(getEdge(forwardEdgeId), getEdge(backwardEdgeId));
}
@Override
public void makeInverseEdges(IEdge forwardEdge, IEdge backwardEdge) {
if (!forwardEdge.equals(this.getInverseEdge(backwardEdge)))
this.inverseEdgeMapping.put(forwardEdge, backwardEdge);
}
/**
* Computes subgraph of the currently known local view. This operation
* changes the current graph instead of cloning it.
......@@ -414,7 +447,7 @@ public class BasicGraph implements Graph {
if (!kHopNodes.contains(node.getId())) {
nodesToBeRemoved.add(node);
}
}
}
for (final INode node : nodesToBeRemoved) {
g.removeElement(node);
}
......@@ -427,12 +460,14 @@ public class BasicGraph implements Graph {
@Override
public void clear() {
this.nodes.clear();
this.edgesById.clear();
this.nodesById.clear();
this.edges.clear();
this.edgesById.clear();
this.incomingAdjacencyList.clear();
this.outgoingAdjacencyList.clear();
this.predecessorNodes.clear();
this.successorNodes.clear();
this.inverseEdgeMapping.clear();
}
/**
......@@ -443,8 +478,7 @@ public class BasicGraph implements Graph {
* for the neighborhood
*/
@Override
public Set<INodeID> getNeighbors(INodeID center, int k,
boolean directedNeighborhood) {
public Set<INodeID> getNeighbors(INodeID center, int k, boolean directedNeighborhood) {
// assert (getNeighbors(center, k, directedNeighborhood, new
// LinkedHashSet<INodeID>())
// .equals(getNeighborsIter(center, k, directedNeighborhood)));
......@@ -458,8 +492,7 @@ public class BasicGraph implements Graph {
*/
private boolean checkGraphConsistency() {
for (IEdge directedEdge : edges) {
if (!nodesById.containsKey(directedEdge.fromId())
|| !nodesById.containsKey(directedEdge.toId())) {
if (!nodesById.containsKey(directedEdge.fromId()) || !nodesById.containsKey(directedEdge.toId())) {
return false;
}
}
......@@ -468,41 +501,39 @@ public class BasicGraph implements Graph {
}
// helper method
private Set<INodeID> getNeighborsIter(INodeID node, int k,
boolean directedNeighborhood) {
private Set<INodeID> getNeighborsIter(INodeID node, int k, boolean directedNeighborhood) {
HashSet<INodeID> visited = new HashSet<INodeID>();
HashSet<INodeID> visited = new HashSet<INodeID>();
// visited.add(node);
if (k < 0)
throw new IllegalArgumentException("k must be >=0, but was " + k);
if (k == 0) {
return visited;
}
if (k < 0)
throw new IllegalArgumentException("k must be >=0, but was " + k);
if (k == 0) {
return visited;
}
HashSet<INodeID> neighbors = new HashSet<INodeID>();
HashSet<INodeID> newNeighbors = new HashSet<INodeID>();
neighbors.add(node);
for (int i = k; i > 0; i--) {
for (INodeID neighbor : neighbors) {
if (visited.add(neighbor))
newNeighbors.addAll(getNeighbors(neighbor, directedNeighborhood));
}
}
neighbors = new HashSet<INodeID>(newNeighbors);
newNeighbors.clear();
}
visited.addAll(neighbors);
return visited;
}
// helper method, replaced by getNeighborsIter which is thought to be more
// efficient
@SuppressWarnings("unused")
private Set<INodeID> getNeighbors(INodeID node, int k,
boolean directedNeighborhood, HashSet<INodeID> visited) {
private Set<INodeID> getNeighbors(INodeID node, int k, boolean directedNeighborhood, HashSet<INodeID> visited) {
if (k < 0)
throw new IllegalArgumentException("k must be >=0, but was " + k);
......@@ -521,8 +552,7 @@ public class BasicGraph implements Graph {
for (INodeID neighbor : getNeighbors(node, directedNeighborhood)) {
if (!visited.contains(neighbor))
neighbors.addAll(getNeighbors(neighbor, k - 1,
directedNeighborhood, visited));
neighbors.addAll(getNeighbors(neighbor, k - 1, directedNeighborhood, visited));
}
return neighbors;
......@@ -531,20 +561,17 @@ public class BasicGraph implements Graph {
@Override
public Set<IEdge> getOutgoingEdges(INodeID node) {
ensureThatNodeIsInGraph(node);
return Collections
.unmodifiableSet(this.outgoingAdjacencyList.get(node));
}
return Collections.unmodifiableSet(this.outgoingAdjacencyList.get(node));
}
@Override
public Set<IEdge> getIncomingEdges(INodeID node) {
ensureThatNodeIsInGraph(node);
return Collections
.unmodifiableSet(this.incomingAdjacencyList.get(node));
}
return Collections.unmodifiableSet(this.incomingAdjacencyList.get(node));
}
@Override
public Set<INodeID> getNeighbors(INodeID node,
boolean directedNeighborhood) {
public Set<INodeID> getNeighbors(INodeID node, boolean directedNeighborhood) {
if (directedNeighborhood)
return Collections.unmodifiableSet(successorNodes.get(node));
@SuppressWarnings("unchecked")
......@@ -565,8 +592,7 @@ public class BasicGraph implements Graph {
private void ensureThatNodeIsInGraph(INodeID node) {
if (!this.nodesById.containsKey(node))
throw new IllegalStateException(
"Node " + node + " is not contained in the graph");
throw new IllegalStateException("Node " + node + " is not contained in the graph");
}
@SuppressWarnings("unused")
......@@ -616,7 +642,7 @@ public class BasicGraph implements Graph {
for (IEdge edge : edges) {
this.addEdge(edge);
}
}
}
@Override
public void addNodes(Iterable<? extends INode> nodes) {
......@@ -700,8 +726,7 @@ public class BasicGraph implements Graph {
}
@Override
public Set<INodeID> getNeighbors(INode node, int k,
boolean directedNeighborhood) {
public Set<INodeID> getNeighbors(INode node, int k, boolean directedNeighborhood) {
return getNeighbors(node.getId(), k, directedNeighborhood);
}
......@@ -712,8 +737,7 @@ public class BasicGraph implements Graph {
@Override
public boolean equalsAttributes(Graph otherGraph) {
if (otherGraph.getNodeCount() != this.getNodeCount()
|| otherGraph.getEdgeCount() != this.getEdgeCount())
if (otherGraph.getNodeCount() != this.getNodeCount() || otherGraph.getEdgeCount() != this.getEdgeCount())
return false;
for (final INode node : this.getNodes()) {
if (!otherGraph.containsNode(node))
......@@ -736,8 +760,7 @@ public class BasicGraph implements Graph {
reverseEdgeSet.add(new DirectedEdge(edge.toId(), edge.fromId()));
}
for (final IEdge edge : this.getEdges()) {
if (!this
.containsEdge(new DirectedEdge(edge.fromId(), edge.toId())))
if (!this.containsEdge(new DirectedEdge(edge.fromId(), edge.toId())))
return false;
}
......@@ -748,8 +771,7 @@ public class BasicGraph implements Graph {
public Iterable<INodeID> getIsolatedNodes() {
final List<INodeID> isolatedNodes = new ArrayList<>();
for (final INode node : this.getNodes()) {
if (this.getOutdegree(node.getId()) == 0
&& this.getIndegree(node.getId()) == 0) {
if (this.getOutdegree(node.getId()) == 0 && this.getIndegree(node.getId()) == 0) {
isolatedNodes.add(node.getId());
}
}
......
......@@ -119,7 +119,7 @@ public interface Graph extends Cloneable {
* already
*/
void addEdges(Iterable<? extends IEdge> edges);
/**
* Adds the given node to the graph
*
......@@ -251,11 +251,58 @@ public interface Graph extends Cloneable {
/**
* Returns the edge with the given ID.
*
* @param edgeID
* @return
* @return edge or null
*/
public IEdge getEdge(EdgeID edgeID);
/**
* Returns the inverse edg eof the edge with the given ID.
*
* @return the inverse edge if exists. Otherwise, null.
* @throws IllegalArgumentException
* if no edge with the given ID is known
* @since 2.5
*/
public IEdge getInverseEdge(EdgeID edgeId);
/**
* Returns the inverse edg eof the edge with the given ID.
*
* <b>Note</b> Edges need to be marked <b>explicitly</b> as inverse edge of
* one another by using {@link #makeInverseEdges(EdgeID, EdgeID)} or
* {@link #makeInverseEdges(IEdge, IEdge)}
*
* @return the inverse edge if exists. Otherwise, null.
* @throws IllegalArgumentException
* if no edge with the given ID is known
* @since 2.5
*/
public IEdge getInverseEdge(IEdge edge);
/**
* @see #makeInverseEdges(IEdge, IEdge)
* @throws IllegalArgumentException
* if one of the given IDs cannot be mapped to an edge
*
* @since 2.5
*/
public void makeInverseEdges(EdgeID forwardEdgeId, EdgeID backwardEdgeId);
/**
* Marks the given pair of edges as inverse edges of each other
*
* Only edges that have been passed to
* {@link #makeInverseEdges(IEdge, IEdge)} will afterwards be returned by
* {@link #getInverseEdge(IEdge)}.
*
* @param forwardEdge
* the first edge
* @param backwardEdge
* the second edge
* @since 2.5
*/
public void makeInverseEdges(IEdge forwardEdge, IEdge backwardEdge);
/*
* Convenience methods
*/
......@@ -348,8 +395,7 @@ public interface Graph extends Cloneable {
* If the directedNeighborhood flag is true, edge directions are considered
* for the neighborhood
*/
public Set<INodeID> getNeighbors(INodeID node, int k,
boolean directedNeighborhood);
public Set<INodeID> getNeighbors(INodeID node, int k, boolean directedNeighborhood);
/**
* Returns the nodes contained in the k-hop neighborhood of the specified
......@@ -358,8 +404,7 @@ public interface Graph extends Cloneable {
* If the directedNeighborhood flag is true, edge directions are considered
* for the neighborhood
*/
public Set<INodeID> getNeighbors(INode node, int k,
boolean directedNeighborhood);
public Set<INodeID> getNeighbors(INode node, int k, boolean directedNeighborhood);
/**
* returns the strict 1-hop neighbors of the specified node.
......@@ -370,8 +415,7 @@ public interface Graph extends Cloneable {
*
* @precondition node is contained in the graph
*/
public Set<INodeID> getNeighbors(INodeID node,
boolean directedNeighborhood);
public Set<INodeID> getNeighbors(INodeID node, boolean directedNeighborhood);
public Set<INodeID> getNeighbors(INode node, boolean directedNeighborhood);
......@@ -458,5 +502,4 @@ public interface Graph extends Cloneable {
*/
public int getDegree(INodeID nodeID);
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment