/** {@inheritDoc} */ protected void encounterVertexAgain(V vertex, E edge) { super.encounterVertexAgain(vertex, edge); int i; if (root != null) { // For rooted detection, the path must either // double back to the root, or to a node of a cycle // which has already been detected. if (vertex.equals(root)) { i = 0; } else if ((cycleSet != null) && cycleSet.contains(vertex)) { i = 0; } else { return; } } else { i = path.indexOf(vertex); } if (i > -1) { if (cycleSet == null) { // we're doing yes/no cycle detection throw new CycleDetectedException(); } else { for (; i < path.size(); ++i) { cycleSet.add(path.get(i)); } } } }
/** * Compute the unique decomposition of the input graph G (atoms of G). Implementation of algorithm * Atoms as described in Berry et al. (2010), DOI:10.3390/a3020197, <a * href="http://www.mdpi.com/1999-4893/3/2/197">http://www.mdpi.com/1999-4893/3/2/197</a> */ private void computeAtoms() { if (chordalGraph == null) { computeMinimalTriangulation(); } separators = new HashSet<>(); // initialize g' as subgraph of graph (same vertices and edges) UndirectedGraph<V, E> gprime = copyAsSimpleGraph(graph); // initialize h' as subgraph of chordalGraph (same vertices and edges) UndirectedGraph<V, E> hprime = copyAsSimpleGraph(chordalGraph); atoms = new HashSet<>(); Iterator<V> iterator = meo.descendingIterator(); while (iterator.hasNext()) { V v = iterator.next(); if (generators.contains(v)) { Set<V> separator = new HashSet<>(Graphs.neighborListOf(hprime, v)); if (isClique(graph, separator)) { if (separator.size() > 0) { if (separators.contains(separator)) { fullComponentCount.put(separator, fullComponentCount.get(separator) + 1); } else { fullComponentCount.put(separator, 2); separators.add(separator); } } UndirectedGraph<V, E> tmpGraph = copyAsSimpleGraph(gprime); tmpGraph.removeAllVertices(separator); ConnectivityInspector<V, E> con = new ConnectivityInspector<>(tmpGraph); if (con.isGraphConnected()) { throw new RuntimeException("separator did not separate the graph"); } for (Set<V> component : con.connectedSets()) { if (component.contains(v)) { gprime.removeAllVertices(component); component.addAll(separator); atoms.add(new HashSet<>(component)); assert (component.size() > 0); break; } } } } hprime.removeVertex(v); } if (gprime.vertexSet().size() > 0) { atoms.add(new HashSet<>(gprime.vertexSet())); } }
private V lowestCommonAncestor(V a, V b) { Set<V> seen = new HashSet<>(); for (; ; ) { a = contracted.get(a); seen.add(a); if (!match.containsKey(a)) { break; } a = path.get(match.get(a)); } for (; ; ) { b = contracted.get(b); if (seen.contains(b)) { return b; } b = path.get(match.get(b)); } }
/** @see Graph#edgesOf(Object) */ public Set<E> edgesOf(V vertex) { ArrayUnenforcedSet<E> inAndOut = new ArrayUnenforcedSet<E>(getEdgeContainer(vertex).incoming); inAndOut.addAll(getEdgeContainer(vertex).outgoing); // we have two copies for each self-loop - remove one of them. if (allowingLoops) { Set<E> loops = getAllEdges(vertex, vertex); for (int i = 0; i < inAndOut.size(); ) { Object e = inAndOut.get(i); if (loops.contains(e)) { inAndOut.remove(i); loops.remove(e); // so we remove it only once } else { i++; } } } return Collections.unmodifiableSet(inAndOut); }
/** * Runs the algorithm on the input graph and returns the match edge set. * * @return set of Edges */ private Set<E> findMatch() { Set<E> result = new ArrayUnenforcedSet<>(); match = new HashMap<>(); path = new HashMap<>(); contracted = new HashMap<>(); for (V i : graph.vertexSet()) { // Any augmenting path should start with _exposed_ vertex // (vertex may not escape match-set being added once) if (!match.containsKey(i)) { // Match is maximal iff graph G contains no more augmenting paths V v = findPath(i); while (v != null) { V pv = path.get(v); V ppv = match.get(pv); match.put(v, pv); match.put(pv, v); v = ppv; } } } Set<V> seen = new HashSet<>(); graph .vertexSet() .stream() .filter(v -> !seen.contains(v) && match.containsKey(v)) .forEach( v -> { seen.add(v); seen.add(match.get(v)); result.add(graph.getEdge(v, match.get(v))); }); return result; }
private V findPath(V root) { Set<V> used = new HashSet<>(); Queue<V> q = new ArrayDeque<>(); // Expand graph back from its contracted state path.clear(); contracted.clear(); graph.vertexSet().forEach(vertex -> contracted.put(vertex, vertex)); used.add(root); q.add(root); while (!q.isEmpty()) { V v = q.remove(); for (E e : graph.edgesOf(v)) { V to = graph.getEdgeSource(e); if (to.equals(v)) { to = graph.getEdgeTarget(e); } if ((contracted.get(v).equals(contracted.get(to))) || to.equals(match.get(v))) { continue; } // Check whether we've hit a 'blossom' if ((to.equals(root)) || ((match.containsKey(to)) && (path.containsKey(match.get(to))))) { V stem = lowestCommonAncestor(v, to); Set<V> blossom = new HashSet<>(); markPath(v, to, stem, blossom); markPath(to, v, stem, blossom); graph .vertexSet() .stream() .filter(i -> contracted.containsKey(i) && blossom.contains(contracted.get(i))) .forEach( i -> { contracted.put(i, stem); if (!used.contains(i)) { used.add(i); q.add(i); } }); // Check whether we've had hit a loop (of even length (!) presumably) } else if (!path.containsKey(to)) { path.put(to, v); if (!match.containsKey(to)) { return to; } to = match.get(to); used.add(to); q.add(to); } } } return null; }