// GraphTea Project: http://github.com/graphtheorysoftware/GraphTea
// Copyright (C) 2012 Graph Theory Software Foundation: http://GraphTheorySoftware.com
// Copyright (C) 2008 Mathematical Science Department of Sharif University of Technology
// Distributed under the terms of the GNU Lesser General Public License (LGPL): http://www.gnu.org/licenses/
package graphtea.library.algorithms;
import graphtea.library.BaseEdge;
import graphtea.library.BaseGraph;
import graphtea.library.BaseVertex;
import graphtea.library.algorithms.util.LibraryUtils;
import graphtea.library.event.MessageEvent;
import graphtea.library.event.typedef.BaseGraphRequest;
import graphtea.platform.lang.Pair;
import java.util.*;
/**
* this class contains some basic algorithms of Directed Acyclic Graphs
*
* @author Azin Azadi
* @author Omid Aladini
*/
public class DAG extends Algorithm implements AutomatedAlgorithm {
public static <VertexType extends BaseVertex, EdgeType extends BaseEdge<VertexType>>
AbstractList<VertexType>
doSort(BaseGraph<VertexType, EdgeType> graph) {
ArrayList<VertexType> alv = new ArrayList<>();
ArrayList<VertexType> out = new ArrayList<>();
LibraryUtils.falsifyVertexMarks(graph);
for (VertexType v : graph)
if (graph.getInDegree(v) == 0) {
alv.add(v);
v.setMark(true);
}
while (alv.size() != 0) {
VertexType v = alv.remove(0);
out.add(v);
A:
for (VertexType target : graph.getNeighbors(v)) {
if (target.getMark())
continue;
for (VertexType src : graph.getBackNeighbours(target)) {
if (!src.getMark()) {
continue A;
}
}
target.setMark(true);
alv.add(target);
}
}
for (VertexType v : graph) {
if (!v.getMark()) {
return null; //graph has a loop
}
}
return out;
}
/**
* finds a maximal length path in the given DAG
*
* @param graph The given graph
* @return null if it is not a DAG, else a list of pairs, which ret[i].first is the parent of the longest path
* of the vertex with Id i, and ret[i].second is the longest path length.
*/
public static <Vertex extends BaseVertex, Edge extends BaseEdge<Vertex>>
AbstractList<Pair<Vertex, Integer>>
findLongestPath(BaseGraph<Vertex, Edge> graph) {
int[] maxPath = new int[graph.getVerticesCount()];
BaseVertex[] parent = new BaseVertex[maxPath.length];
AbstractList<Vertex> sort = doSort(graph);
if (sort == null)
return null;
//the greedy section
for (Vertex v : sort) {
int id = v.getId();
if (graph.getInDegree(v) == 0) {
maxPath[id] = 0;
parent[id] = null;
} else {
int max = -1;
Vertex maxV = null;
for (Vertex src : graph.getBackNeighbours(v)) {
if (max < maxPath[src.getId()] + 1) {
max = maxPath[src.getId()] + 1;
maxV = src;
}
}
maxPath[id] = max;
parent[id] = maxV;
}
}
AbstractList<Pair<Vertex, Integer>> ret = new ArrayList<>(maxPath.length);
for (int i = 0; i < maxPath.length; i++) {
ret.add(new Pair<>((Vertex) parent[i], maxPath[i]));
}
return ret;
}
/**
*
* @return the list of vertices which can be visited by starting a traverse from root
*/
public static <Vertex extends BaseVertex, Edge extends BaseEdge<Vertex>>
List<Vertex>
getTraversableSubGraph(BaseGraph<Vertex, Edge> graph, Vertex root){
LibraryUtils.falsifyVertexMarks(graph);
List<Vertex> ret= new ArrayList<>();
Queue<Vertex> q = new LinkedList<>();
q.add(root);
root.setMark(true);
while (!q.isEmpty()){
Vertex v = q.poll();
ret.add(v);
for (Vertex nv:graph.getNeighbors(v)){
if (!nv.getMark()){
q.add(nv);
nv.setMark(true);
}
}
}
return ret;
}
/**
* This method returns a cycle in the given2t directed graph which is a proof that it is not a DAG,
*
* @param graph The input graph
* @return a cycle in the graph if it is NOT a dag or null if it is a dag
*/
public static <Vertex extends BaseVertex, Edge extends BaseEdge<Vertex>>
LinkedList<Vertex>
findACycle(BaseGraph<Vertex, Edge> graph) {
byte[] mark = new byte[graph.getVerticesCount()];
BaseVertex[] V = graph.getVertexArray();
BaseVertex[] parent = new BaseVertex[mark.length];
BaseVertex[] root = new BaseVertex[mark.length];
Integer[] dfsNum = new Integer[mark.length];
LibraryUtils.falsifyVertexMarks(graph);
//start from one vertex and go to it's neighbours [BFS], continue until you meet a MARKed vertex
HashSet<Vertex> visitedVertices = new HashSet<>();
LinkedList<Vertex> bfsStack = new LinkedList<>();
Vertex cycleStart = null;
Vertex cycleEnd = null;
// visitedVertices.add(current);
int scan = 0;
int counter = 0;
Vertex current;
//perform a nonrecursive dfs
for (scan = 0; scan < V.length; scan++) {
Vertex u = (Vertex) V[scan];
if (!u.getMark()) { /* Start a new search from u */
bfsStack.push(u);
root[u.getId()] = u;
while (!bfsStack.isEmpty()) {
Vertex v = bfsStack.pop();
if (!v.getMark()) {
v.setMark(true);
dfsNum[v.getId()] = counter;
counter++;
for (Vertex w : graph.getNeighbors(v))
if (!w.getMark()) {
bfsStack.push(w);
parent[w.getId()] = v;
root[w.getId()] = u;
}
}
}
}
}
//try to fing backedges
for (Edge e : graph.edges()) {
int src = e.source.getId();
int trg = e.target.getId();
if (root[src] == root[trg] && dfsNum[src] > dfsNum[trg]) {
cycleStart = e.target;
cycleEnd = e.source;
current = cycleEnd;
LinkedList<Vertex> ret = new LinkedList<>();
ret.addFirst(current);
while (current != null && current != cycleStart) {
current = (Vertex) parent[current.getId()];
ret.addFirst(current);
}
if (current != null)
return ret;
}
}
return null;
// A:
// while (true) {
// if (bfsStack.isEmpty()) {
// for (; scan < mark.length; scan++) {
// bfsStack.add((Vertex) V[scan]);
// scan++;
// visitedVertices.clear();
// LibraryUtils.falsifyVertexMarks(graph);
// break;
// }
// }
// if (bfsStack.isEmpty()) //no unmarked vertex
// return null;
// current = bfsStack.pop();
// current.setMark(true);
// for (Vertex trg : graph.getNeighbors(current)) {
// if (!trg.getMark()) {
// bfsStack.addLast(trg);
// parent[trg.getId()] = current;
// } else {
// if (visitedVertices.contains(trg)) {
// cycleStart = trg;
// cycleEnd = current;
// break A;
// }
// }
// }
// }
//extract from parents, the path between the cycle start and cycle end
// return ret;
}
/**
* finds all paths in the given DAG from src to trg.
*/
public static <Vertex extends BaseVertex, Edge extends BaseEdge<Vertex>>
Vector<Stack<Vertex>> findAllPaths(BaseGraph<Vertex, Edge> dag, Vertex src, Vertex trg) {
Vector<Stack<Vertex>> ret = new Vector<>();
findAllPathsRec(dag, src, trg, ret, new Stack<>());
return ret;
}
private static <Vertex extends BaseVertex, Edge extends BaseEdge<Vertex>>
void findAllPathsRec(BaseGraph<Vertex, Edge> graph, Vertex src, Vertex trg, Vector<Stack<Vertex>> ret, Stack<Vertex> currentPath) {
currentPath.push(src);
if (src == trg) {
ret.add((Stack<Vertex>) currentPath.clone());
} else {
for (Vertex v : graph.getNeighbors(src)) {
findAllPathsRec(graph, v, trg, ret, currentPath);
}
}
currentPath.pop();
}
/**
* finds all ancestors of src in the given DAG.
* closer ancestors to src come first in returned vector
*/
public static <Vertex extends BaseVertex, Edge extends BaseEdge<Vertex>>
Vector<Vertex> findAllAncestors(BaseGraph<Vertex, Edge> dag, Vertex src) {
Vector<Vertex> ret = new Vector<>();
Queue<Vertex> q = new LinkedList<>();
q.add(src);
while (!q.isEmpty()) {
Vertex cur = q.poll();
for (Vertex anc : dag.getBackNeighbours(cur)) {
q.add(anc);
ret.add(anc);
}
}
return ret;
}
public void doAlgorithm() {
BaseGraphRequest gr = new BaseGraphRequest();
dispatchEvent(gr);
BaseGraph<BaseVertex, BaseEdge<BaseVertex>> graph = gr.getGraph();
AbstractList<BaseVertex> alv = doSort(graph);
if (alv == null)
dispatchEvent(new MessageEvent("Graph has a cycle"));
else {
String s = "Topological sort sequence:";
for (BaseVertex v : alv)
s += v + ", ";
dispatchEvent(new MessageEvent(s));
}
}
}