All Downloads are FREE. Search and download functionalities are using the official Maven repository.

edu.stanford.nlp.graph.DirectedMultiGraph Maven / Gradle / Ivy

Go to download

Stanford Parser processes raw text in English, Chinese, German, Arabic, and French, and extracts constituency parse trees.

There is a newer version: 3.9.2
Show newest version
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);
  }

  /**
   * Deletes all duplicate edges.
   */
  public void deleteDuplicateEdges() {
    for (V vertex : getAllVertices()) {
      for (V vertex2 : outgoingEdges.get(vertex).keySet()) {
        List data = outgoingEdges.get(vertex).get(vertex2);
        Set deduplicatedData = new TreeSet<>(data);
        data.clear();
        data.addAll(deduplicatedData);
      }
      for (V vertex2 : incomingEdges.get(vertex).keySet()) {
        List data = incomingEdges.get(vertex).get(vertex2);
        Set deduplicatedData = new TreeSet<>(data);
        data.clear();
        data.addAll(deduplicatedData);
      }
    }
  }

  
  public Iterator incomingEdgeIterator(final V vertex) {
    return new EdgeIterator<>(vertex, incomingEdges, outgoingEdges);
  }

  public Iterable incomingEdgeIterable(final V vertex) {
    return () -> new EdgeIterator<>(vertex, incomingEdges, outgoingEdges);
  }

  public Iterator outgoingEdgeIterator(final V vertex) {
    return new EdgeIterator<>(vertex, outgoingEdges, incomingEdges);
  }

  public Iterable outgoingEdgeIterable(final V vertex) {
    return () -> new EdgeIterator<>(vertex, outgoingEdges, incomingEdges);
  }

  public Iterator edgeIterator() {
    return new EdgeIterator<>(this);
  }

  public Iterable edgeIterable() {
    return () -> new EdgeIterator<>(DirectedMultiGraph.this);
  }
  

  /**
   * This class handles either iterating over a single vertex's
   * connections or over all connections in a graph.
   */
  static class EdgeIterator implements Iterator {
    private final Map>> reverseEdges;
    /** when iterating over the whole graph, this iterates over nodes */
    private Iterator>>> vertexIterator;
    /** for a given node, this iterates over its neighbors */
    private Iterator>> connectionIterator;
    /** given the neighbor of a node, this iterates over all its connections */
    private Iterator edgeIterator;

    private V currentSource = null;
    private V currentTarget = null;
    private E currentEdge = null;
    private boolean hasNext = true;


    public EdgeIterator(DirectedMultiGraph graph) {
      vertexIterator = graph.outgoingEdges.entrySet().iterator();
      reverseEdges = graph.incomingEdges;
    }

    public EdgeIterator(V startVertex, Map>> source,
                        Map>> reverseEdges) {
      currentSource = startVertex;
      Map> neighbors = source.get(startVertex);
      if (neighbors != null) {
        vertexIterator = null;
        connectionIterator = neighbors.entrySet().iterator();
      }
      this.reverseEdges = reverseEdges;
    }

    @Override
    public boolean hasNext() {
      primeIterator();
      return hasNext;
    }

    @Override
    public E next() {
      if (!hasNext()) {
        throw new NoSuchElementException("Graph edge iterator exhausted.");
      }
      currentEdge = edgeIterator.next();
      return currentEdge;
    }

    private void primeIterator() {
      if (edgeIterator != null && edgeIterator.hasNext()) {
        hasNext = true;  // technically, we shouldn't need to put this here, but let's be safe
      } else if (connectionIterator != null && connectionIterator.hasNext()) {
        Map.Entry> nextConnection = connectionIterator.next();
        edgeIterator = nextConnection.getValue().iterator();
        currentTarget = nextConnection.getKey();
        primeIterator();
      } else if (vertexIterator != null && vertexIterator.hasNext()) {
        Map.Entry>> nextVertex = vertexIterator.next();
        connectionIterator = nextVertex.getValue().entrySet().iterator();
        currentSource = nextVertex.getKey();
        primeIterator();
      } else {
        hasNext = false;
      }
    }

    @Override
    public void remove() {
      if (currentEdge != null) {
        reverseEdges.get(currentTarget).get(currentSource).remove(currentEdge);
        edgeIterator.remove();

        if (reverseEdges.get(currentTarget).get(currentSource) != null &&
            reverseEdges.get(currentTarget).get(currentSource).size() == 0) {
          connectionIterator.remove();
          reverseEdges.get(currentTarget).remove(currentSource);
          // TODO: may not be necessary to set this to null
          edgeIterator = null;
        }
      }
    }
  }

  /**
   * Topological sort of the graph.
   * 
* This method uses the depth-first search implementation of * topological sort. * Topological sorting only works if the graph is acyclic. * * @return A sorted list of the vertices * @throws IllegalStateException if this graph is not a DAG */ public List topologicalSort() { List result = Generics.newArrayList(); Set temporary = outerMapFactory.newSet(); Set permanent = outerMapFactory.newSet(); for (V vertex : getAllVertices()) { if (!temporary.contains(vertex)) { topologicalSortHelper(vertex, temporary, permanent, result); } } Collections.reverse(result); return result; } private void topologicalSortHelper(V vertex, Set temporary, Set permanent, List result) { temporary.add(vertex); Map> neighborMap = outgoingEdges.get(vertex); if (neighborMap != null) { for (V neighbor : neighborMap.keySet()) { if (permanent.contains(neighbor)) { continue; } if (temporary.contains(neighbor)) { throw new IllegalStateException("This graph has cycles. Topological sort not possible: " + this.toString()); } topologicalSortHelper(neighbor, temporary, permanent, result); } } result.add(vertex); permanent.add(vertex); } /** * Cast this multi-graph as a map from vertices, to the outgoing data along edges out of those vertices. * * @return A map representation of the graph. */ public Map> toMap() { Map> map = innerMapFactory.newMap(); for (V vertex : getAllVertices()) { map.put(vertex, getOutgoingEdges(vertex)); } return map; } @Override public String toString() { StringBuilder s = new StringBuilder(); s.append("{\n"); s.append("Vertices:\n"); for (V vertex : outgoingEdges.keySet()) { s.append(" ").append(vertex).append('\n'); } s.append("Edges:\n"); for (V source : outgoingEdges.keySet()) { for (V dest : outgoingEdges.get(source).keySet()) { for (E edge : outgoingEdges.get(source).get(dest)) { s.append(" ").append(source).append(" -> ").append(dest).append(" : ").append(edge).append('\n'); } } } s.append('}'); return s.toString(); } private static final long serialVersionUID = 609823567298345145L; }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy