edu.stanford.nlp.graph.DirectedMultiGraph Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of stanford-parser Show documentation
Show all versions of stanford-parser Show documentation
Stanford Parser processes raw text in English, Chinese, German, Arabic, and French, and extracts constituency parse trees.
package edu.stanford.nlp.graph;
import java.util.*;
import edu.stanford.nlp.util.CollectionUtils;
import edu.stanford.nlp.util.Generics;
import edu.stanford.nlp.util.MapFactory;
/**
* Simple graph library; this is directed for now. This class focuses on time
* efficiency rather than memory efficiency.
*
* @author sonalg
* @author John Bauer
*
* @param
* Type of vertices
* @param
* Type of edges.
*/
public class DirectedMultiGraph implements Graph /* Serializable */{
final Map>> outgoingEdges;
final Map>> incomingEdges;
final MapFactory>> outerMapFactory;
final MapFactory> innerMapFactory;
public DirectedMultiGraph() {
this(MapFactory.>>hashMapFactory(), MapFactory.>hashMapFactory());
}
public DirectedMultiGraph(MapFactory>> outerMapFactory, MapFactory> innerMapFactory) {
this.outerMapFactory = outerMapFactory;
this.innerMapFactory = innerMapFactory;
this.outgoingEdges = outerMapFactory.newMap();
this.incomingEdges = outerMapFactory.newMap();
}
/**
* Creates a copy of the given graph. This will copy the entire data structure (this may be slow!), but will not copy
* any of the edge or vertex objects.
*
* @param graph The graph to copy into this object.
*/
public DirectedMultiGraph(DirectedMultiGraph graph) {
this(graph.outerMapFactory, graph.innerMapFactory);
for (Map.Entry>> map : graph.outgoingEdges.entrySet()) {
Map> edgesCopy = innerMapFactory.newMap();
for (Map.Entry> entry : map.getValue().entrySet()) {
edgesCopy.put(entry.getKey(), Generics.newArrayList(entry.getValue()));
}
this.outgoingEdges.put(map.getKey(), edgesCopy);
}
for (Map.Entry>> map : graph.incomingEdges.entrySet()) {
Map> edgesCopy = innerMapFactory.newMap();
for (Map.Entry> entry : map.getValue().entrySet()) {
edgesCopy.put(entry.getKey(), Generics.newArrayList(entry.getValue()));
}
this.incomingEdges.put(map.getKey(), edgesCopy);
}
}
/**
* Be careful hashing these. They are mutable objects, and changing the object
* will throw off the hash code, messing up your hash table
*/
public int hashCode() {
return outgoingEdges.hashCode();
}
public boolean equals(Object that) {
if (that == this)
return true;
if (!(that instanceof DirectedMultiGraph))
return false;
return outgoingEdges.equals(((DirectedMultiGraph, ?>) that).outgoingEdges);
}
/**
* For adding a zero degree vertex
*
* @param v
*/
@Override
public boolean addVertex(V v) {
if (outgoingEdges.containsKey(v))
return false;
outgoingEdges.put(v, innerMapFactory.newMap());
incomingEdges.put(v, innerMapFactory.newMap());
return true;
}
private Map> getOutgoingEdgesMap(V v) {
Map> map = outgoingEdges.get(v);
if (map == null) {
map = innerMapFactory.newMap();
outgoingEdges.put(v, map);
incomingEdges.put(v, innerMapFactory.newMap());
}
return map;
}
private Map> getIncomingEdgesMap(V v) {
Map> map = incomingEdges.get(v);
if (map == null) {
outgoingEdges.put(v, innerMapFactory.newMap());
map = innerMapFactory.newMap();
incomingEdges.put(v, map);
}
return map;
}
/**
* adds vertices (if not already in the graph) and the edge between them
*
* @param source
* @param dest
* @param data
*/
@Override
public void add(V source, V dest, E data) {
Map> outgoingMap = getOutgoingEdgesMap(source);
Map> incomingMap = getIncomingEdgesMap(dest);
List outgoingList = outgoingMap.get(dest);
if (outgoingList == null) {
outgoingList = new ArrayList();
outgoingMap.put(dest, outgoingList);
}
List incomingList = incomingMap.get(source);
if (incomingList == null) {
incomingList = new ArrayList();
incomingMap.put(source, incomingList);
}
outgoingList.add(data);
incomingList.add(data);
}
@Override
public boolean removeEdges(V source, V dest) {
if (!outgoingEdges.containsKey(source)) {
return false;
}
if (!incomingEdges.containsKey(dest)) {
return false;
}
if (!outgoingEdges.get(source).containsKey(dest)) {
return false;
}
outgoingEdges.get(source).remove(dest);
incomingEdges.get(dest).remove(source);
return true;
}
@Override
public boolean removeEdge(V source, V dest, E data) {
if (!outgoingEdges.containsKey(source)) {
return false;
}
if (!incomingEdges.containsKey(dest)) {
return false;
}
if (!outgoingEdges.get(source).containsKey(dest)) {
return false;
}
boolean foundOut = outgoingEdges.containsKey(source) && outgoingEdges.get(source).containsKey(dest) &&
outgoingEdges.get(source).get(dest).remove(data);
boolean foundIn = incomingEdges.containsKey(dest) && incomingEdges.get(dest).containsKey(source) &&
incomingEdges.get(dest).get(source).remove(data);
if (foundOut && !foundIn) {
throw new AssertionError("Edge found in outgoing but not incoming");
}
if (foundIn && !foundOut) {
throw new AssertionError("Edge found in incoming but not outgoing");
}
// TODO: cut down the number of .get calls
if (outgoingEdges.containsKey(source) && (!outgoingEdges.get(source).containsKey(dest) || outgoingEdges.get(source).get(dest).size() == 0)) {
outgoingEdges.get(source).remove(dest);
}
if (incomingEdges.containsKey(dest) && (!incomingEdges.get(dest).containsKey(source) || incomingEdges.get(dest).get(source).size() == 0)) {
incomingEdges.get(dest).remove(source);
}
return foundOut;
}
/**
* remove a vertex (and its edges) from the graph.
*
* @param vertex
* @return true if successfully removes the node
*/
@Override
public boolean removeVertex(V vertex) {
if (!outgoingEdges.containsKey(vertex)) {
return false;
}
for (V other : outgoingEdges.get(vertex).keySet()) {
incomingEdges.get(other).remove(vertex);
}
for (V other : incomingEdges.get(vertex).keySet()) {
outgoingEdges.get(other).remove(vertex);
}
outgoingEdges.remove(vertex);
incomingEdges.remove(vertex);
return true;
}
@Override
public boolean removeVertices(Collection vertices) {
boolean changed = false;
for (V v : vertices) {
if (removeVertex(v)) {
changed = true;
}
}
return changed;
}
@Override
public int getNumVertices() {
return outgoingEdges.size();
}
@Override
public List getOutgoingEdges(V v) {
if (!outgoingEdges.containsKey(v)) { //noinspection unchecked
return Collections.emptyList();
}
return CollectionUtils.flatten(outgoingEdges.get(v).values());
}
@Override
public List getIncomingEdges(V v) {
if (!incomingEdges.containsKey(v)) { //noinspection unchecked
return Collections.emptyList();
}
return CollectionUtils.flatten(incomingEdges.get(v).values());
}
@Override
public int getNumEdges() {
int count = 0;
for (Map.Entry>> sourceEntry : outgoingEdges.entrySet()) {
for (Map.Entry> destEntry : sourceEntry.getValue().entrySet()) {
count += destEntry.getValue().size();
}
}
return count;
}
@Override
public Set getParents(V vertex) {
Map> parentMap = incomingEdges.get(vertex);
if (parentMap == null)
return null;
return Collections.unmodifiableSet(parentMap.keySet());
}
@Override
public Set getChildren(V vertex) {
Map> childMap = outgoingEdges.get(vertex);
if (childMap == null)
return null;
return Collections.unmodifiableSet(childMap.keySet());
}
/**
* Gets both parents and children nodes
*
* @param v
*/
@Override
public Set getNeighbors(V v) {
// TODO: pity we have to copy the sets... is there a combination set?
Set children = getChildren(v);
Set parents = getParents(v);
if (children == null && parents == null)
return null;
Set neighbors = innerMapFactory.newSet();
neighbors.addAll(children);
neighbors.addAll(parents);
return neighbors;
}
/**
* clears the graph, removes all edges and nodes
*/
@Override
public void clear() {
incomingEdges.clear();
outgoingEdges.clear();
}
@Override
public boolean containsVertex(V v) {
return outgoingEdges.containsKey(v);
}
/**
* only checks if there is an edge from source to dest. To check if it is
* connected in either direction, use isNeighbor
*
* @param source
* @param dest
*/
@Override
public boolean isEdge(V source, V dest) {
Map> childrenMap = outgoingEdges.get(source);
if (childrenMap == null || childrenMap.isEmpty())
return false;
List edges = childrenMap.get(dest);
if (edges == null || edges.isEmpty())
return false;
return edges.size() > 0;
}
@Override
public boolean isNeighbor(V source, V dest) {
return isEdge(source, dest) || isEdge(dest, source);
}
@Override
public Set getAllVertices() {
return Collections.unmodifiableSet(outgoingEdges.keySet());
}
@Override
public List getAllEdges() {
List edges = new ArrayList();
for (Map> e : outgoingEdges.values()) {
for (List ee : e.values()) {
edges.addAll(ee);
}
}
return edges;
}
/**
* False if there are any vertices in the graph, true otherwise. Does not care
* about the number of edges.
*/
@Override
public boolean isEmpty() {
return outgoingEdges.isEmpty();
}
/**
* Deletes nodes with zero incoming and zero outgoing edges
*/
@Override
public void removeZeroDegreeNodes() {
List toDelete = new ArrayList();
for (V vertex : outgoingEdges.keySet()) {
if (outgoingEdges.get(vertex).isEmpty() && incomingEdges.get(vertex).isEmpty()) {
toDelete.add(vertex);
}
}
for (V vertex : toDelete) {
outgoingEdges.remove(vertex);
incomingEdges.remove(vertex);
}
}
@Override
public List getEdges(V source, V dest) {
Map> childrenMap = outgoingEdges.get(source);
if (childrenMap == null) {
return Collections.emptyList();
}
List edges = childrenMap.get(dest);
if (edges == null) {
return Collections.emptyList();
}
return Collections.unmodifiableList(edges);
}
/**
* direction insensitive (the paths can go "up" or through the parents)
*/
public List getShortestPath(V node1, V node2) {
if (!outgoingEdges.containsKey(node1) || !outgoingEdges.containsKey(node2)) {
return null;
}
return getShortestPath(node1, node2, false);
}
public List getShortestPathEdges(V node1, V node2) {
return convertPath(getShortestPath(node1, node2), false);
}
/**
* can specify the direction sensitivity
*
* @param node1
* @param node2
* @param directionSensitive
* - whether the path can go through the parents
* @return the list of nodes you get through to get there
*/
public List getShortestPath(V node1, V node2, boolean directionSensitive) {
if (!outgoingEdges.containsKey(node1) || !outgoingEdges.containsKey(node2)) {
return null;
}
return DijkstraShortestPath.getShortestPath(this, node1, node2, directionSensitive);
}
public List getShortestPathEdges(V node1, V node2, boolean directionSensitive) {
return convertPath(getShortestPath(node1, node2, directionSensitive), directionSensitive);
}
public List convertPath(List nodes, boolean directionSensitive) {
if (nodes == null)
return null;
if (nodes.size() <= 1)
return Collections.emptyList();
List path = new ArrayList();
Iterator nodeIterator = nodes.iterator();
V previous = nodeIterator.next();
while (nodeIterator.hasNext()) {
V next = nodeIterator.next();
E connection = null;
List edges = getEdges(previous, next);
if (edges.size() == 0 && !directionSensitive) {
edges = getEdges(next, previous);
}
if (edges.size() > 0) {
connection = edges.get(0);
} else {
throw new IllegalArgumentException("Path given with missing " + "edge connection");
}
path.add(connection);
previous = next;
}
return path;
}
@Override
public int getInDegree(V vertex) {
if (!containsVertex(vertex)) {
return 0;
}
int result = 0;
Map> incoming = incomingEdges.get(vertex);
for (List edges : incoming.values()) {
result += edges.size();
}
return result;
}
@Override
public int getOutDegree(V vertex) {
int result = 0;
Map> outgoing = outgoingEdges.get(vertex);
if (outgoing == null) {
return 0;
}
for (List edges : outgoing.values()) {
result += edges.size();
}
return result;
}
@Override
public List> getConnectedComponents() {
return ConnectedComponents.getConnectedComponents(this);
}
public Iterator incomingEdgeIterator(final V vertex) {
return new EdgeIterator(incomingEdges, vertex);
}
public Iterable incomingEdgeIterable(final V vertex) {
return () -> new EdgeIterator(incomingEdges, vertex);
}
public Iterator outgoingEdgeIterator(final V vertex) {
return new EdgeIterator(outgoingEdges, vertex);
}
public Iterable outgoingEdgeIterable(final V vertex) {
return () -> new EdgeIterator(outgoingEdges, vertex);
}
public Iterator edgeIterator() {
return new EdgeIterator(this);
}
public Iterable edgeIterable() {
return () -> new EdgeIterator(DirectedMultiGraph.this);
}
static class EdgeIterator implements Iterator {
private Iterator