Преглед на файлове

ENH: Added ctkDependencyGraph class allowing to obtain the topological order of DAG.

The class also check for cycle and allow to obtain description of the
discovered cycle.
Jean-Christophe Fillion-Robin преди 15 години
родител
ревизия
9daeadf230
променени са 3 файла, в които са добавени 478 реда и са изтрити 0 реда
  1. 2 0
      Libs/Core/CMakeLists.txt
  2. 409 0
      Libs/Core/ctkDependencyGraph.cxx
  3. 67 0
      Libs/Core/ctkDependencyGraph.h

+ 2 - 0
Libs/Core/CMakeLists.txt

@@ -39,6 +39,8 @@ SET(KIT_include_directories
   
 # Source files
 SET(KIT_SRCS
+  ctkDependencyGraph.cxx
+  ctkDependencyGraph.h
   ctkModelTester.cxx
   ctkModelTester.h
   ctkUtils.cxx

+ 409 - 0
Libs/Core/ctkDependencyGraph.cxx

@@ -0,0 +1,409 @@
+
+// QT includes
+#include <QQueue>
+#include <QVarLengthArray>
+#include <QDebug>
+
+// CTK includes
+#include "ctkDependencyGraph.h"
+
+// STD includes
+#include <iostream>
+
+#define MAXV 100
+#define MAXDEGREE 50
+
+//----------------------------------------------------------------------------
+class ctkDependencyGraph::ctkInternal
+{
+public:
+  ctkInternal(ctkDependencyGraph* p);
+  
+  /// Compute indegree
+  void computeIndegrees(QVarLengthArray<int, MAXV>& computedIndegrees);
+  
+  /// Traverse tree using Depth-first_search
+  void traverseUsingDFS(int v);
+  
+  /// Called each time an edge is visited
+  void processEdge(int from, int to); 
+  
+  /// Called each time a vertex is processed
+  void processVertex(int v);
+  
+  void setEdge(int vertice, int degree, int value);
+  int edge(int vertice, int degree);
+  
+  /// See http://en.wikipedia.org/wiki/Adjacency_list
+  QVarLengthArray<QVarLengthArray<int,MAXDEGREE>*, MAXV+1> Edges;
+  QVarLengthArray<int, MAXV+1> Degree;
+  int NVertices;
+  int NEdges;
+  
+  /// Structure used by DFS
+  /// See http://en.wikipedia.org/wiki/Depth-first_search
+  QVarLengthArray<bool, MAXV> Processed;	// processed vertices
+  QVarLengthArray<bool, MAXV> Discovered; // discovered vertices
+  QVarLengthArray<int, MAXV>  Parent;	    // relation discovered
+  
+  bool    Abort;	// Flag indicating if traverse should be aborted
+  bool    Verbose; 
+  bool    CycleDetected; 
+  int     CycleOrigin; 
+  int     CycleEnd;
+  
+  QList<int> ListOfEdgeToExclude;
+  
+  /// Pointer to the public API
+  ctkDependencyGraph* P;
+};
+
+//----------------------------------------------------------------------------
+// ctkInternal methods
+
+//----------------------------------------------------------------------------
+ctkDependencyGraph::ctkInternal::ctkInternal(ctkDependencyGraph* p)
+{
+  Q_ASSERT(p);
+  this->P = p;
+  this->NVertices = 0; 
+  this->NEdges = 0; 
+  this->Abort = false;
+  this->Verbose = false;
+  this->CycleDetected = false;
+  this->CycleOrigin = 0;
+  this->CycleEnd = 0;
+}
+
+//----------------------------------------------------------------------------
+void ctkDependencyGraph::ctkInternal::computeIndegrees(QVarLengthArray<int, MAXV>& computedIndegrees)
+{
+	for (int i=1; i <= this->NVertices; i++)
+	  {
+	  computedIndegrees[i] = 0;
+	  }
+
+	for (int i=1; i <= this->NVertices; i++) 
+	  {
+		for (int j=0; j < this->Degree[i]; j++) 
+		  {
+		  computedIndegrees[ this->edge(i,j) ] ++;
+		  }
+		}
+}
+
+//----------------------------------------------------------------------------
+void ctkDependencyGraph::ctkInternal::traverseUsingDFS(int v)
+{
+  // allow for search termination
+	if (this->Abort)
+	  {
+	  return;
+	  }
+
+	this->Discovered[v] = true;
+	this->processVertex(v);
+
+  int y; // successor vertex
+	for (int i=0; i<this->Degree[v]; i++)
+	  {
+		y = this->edge(v, i);
+		if (this->P->shouldExcludeEdge(this->edge(v, i)) == false)
+		  {
+			if (this->Discovered[y] == false)
+			  {
+				this->Parent[y] = v;
+				this->traverseUsingDFS(y);
+			  } 
+			else 
+			  {
+				if (this->Processed[y] == false)
+				  {
+					this->processEdge(v,y);
+					}
+			  }
+		  }
+		if (this->Abort)
+		  {
+		  return;
+		  }
+	}
+
+	this->Processed[v] = true;
+}
+
+//----------------------------------------------------------------------------
+void ctkDependencyGraph::ctkInternal::processEdge(int from, int to)
+{
+  if (this->Parent[from] != to)
+    {
+    this->CycleDetected = true;
+    this->CycleOrigin = to; 
+    this->CycleEnd = from;
+    if (this->Verbose)
+      {
+      QList<int> path;
+      this->P->findPath(from, to, path);
+      qWarning() << "Cycle detected from " << to << " to " << from;
+      qWarning() << " " << path;
+      path.clear();
+      this->P->findPath(to, from, path);
+      qWarning() << " " << path;
+      }
+    this->Abort = true;
+    }
+}
+
+//----------------------------------------------------------------------------
+void ctkDependencyGraph::ctkInternal::processVertex(int v)
+{
+	if (this->Verbose)
+	  {
+	  qDebug() << "processed vertex " << v;
+	  }
+}
+
+//----------------------------------------------------------------------------
+void ctkDependencyGraph::ctkInternal::setEdge(int vertice, int degree, int value)
+{
+  Q_ASSERT(vertice <= this->NVertices);
+  Q_ASSERT(degree < MAXDEGREE);
+  (*this->Edges[vertice])[degree] = value; 
+}
+
+//----------------------------------------------------------------------------
+int ctkDependencyGraph::ctkInternal::edge(int vertice, int degree)
+{
+  Q_ASSERT(vertice <= this->NVertices);
+  Q_ASSERT(degree < MAXDEGREE);
+  return (*this->Edges[vertice])[degree];
+}
+    
+//----------------------------------------------------------------------------
+// ctkDependencyGraph methods
+
+//----------------------------------------------------------------------------
+ctkDependencyGraph::ctkDependencyGraph(int nvertices, int nedges)
+{
+  this->Internal = new ctkInternal(this);
+  
+  this->Internal->NVertices = nvertices; 
+  this->Internal->NEdges = nedges;
+  
+  // Resize internal array
+  this->Internal->Processed.resize(nvertices + 1);
+  this->Internal->Discovered.resize(nvertices + 1);
+  this->Internal->Parent.resize(nvertices + 1);
+  this->Internal->Edges.resize(nvertices + 1);
+  this->Internal->Degree.resize(nvertices + 1);
+
+  for (int i=1; i <= nvertices; i++)
+    {
+    this->Internal->Degree[i] = 0;
+    }
+    
+  // initialize Edge adjacency list
+  for (int i=0; i <= nvertices; i++)
+    {
+    this->Internal->Edges[i] = new QVarLengthArray<int, MAXDEGREE>();
+    this->Internal->Edges[i]->resize(MAXDEGREE);
+    }
+    
+  // initialize search
+  for (int i=1; i <= nvertices; i++)
+    {
+    this->Internal->Processed[i] = false;
+    this->Internal->Discovered[i] = false;
+    this->Internal->Parent[i] = -1;
+    }
+}
+
+//----------------------------------------------------------------------------
+ctkDependencyGraph::~ctkDependencyGraph()
+{
+  delete this->Internal; 
+}
+
+//----------------------------------------------------------------------------
+void ctkDependencyGraph::printAdditionalInfo()
+{
+  qDebug() << "ctkDependencyGraph (" << this << ")" << endl
+           << " NVertices:" << this->numberOfVertices() << endl
+           << " NEdges:" << this->numberOfEdges() << endl
+           << " Abort:" << this->Internal->Abort;
+           
+  qDebug() << " [Processed]";
+  for(int i=0; i < this->Internal->Processed.size(); i++)
+    {
+    qDebug() << i << "->" << this->Internal->Processed[i]; 
+    }
+  qDebug() << " [Discovered]";
+  for(int i=0; i < this->Internal->Discovered.size(); i++)
+    {
+    qDebug() << i << "->" << this->Internal->Discovered[i]; 
+    }
+  qDebug() << " [Parent]";
+  for(int i=0; i < this->Internal->Parent.size(); i++)
+    {
+    qDebug() << i << "->" << this->Internal->Parent[i]; 
+    }
+  qDebug() << " [Graph]"; 
+  this->printGraph();
+}
+
+//----------------------------------------------------------------------------
+void ctkDependencyGraph::printGraph()
+{
+  for(int i=1; i <= this->Internal->NVertices; i++)
+    {
+    std::cout << i << ":";
+    for (int j=0; j < this->Internal->Degree[i]; j++)
+      {
+      std::cout << " " << this->Internal->edge(i, j);
+      }
+    std::cout << std::endl;
+    }
+}
+
+//----------------------------------------------------------------------------
+int ctkDependencyGraph::numberOfVertices()
+{
+  return this->Internal->NVertices;
+}
+
+//----------------------------------------------------------------------------
+int ctkDependencyGraph::numberOfEdges()
+{
+  return this->Internal->NEdges;
+}
+
+//----------------------------------------------------------------------------
+void ctkDependencyGraph::setVerbose(bool verbose)
+{
+  this->Internal->Verbose = verbose;
+}
+
+//----------------------------------------------------------------------------
+void ctkDependencyGraph::setEdgeListToExclude(const QList<int>& list)
+{
+  this->Internal->ListOfEdgeToExclude = list;
+}
+
+//----------------------------------------------------------------------------
+bool ctkDependencyGraph::shouldExcludeEdge(int edge)
+{
+  return this->Internal->ListOfEdgeToExclude.contains(edge);
+}
+
+//----------------------------------------------------------------------------
+bool ctkDependencyGraph::checkForCycle()
+{
+  if (this->Internal->NEdges > 0)
+    {
+    // get a valid vertice Id
+    int verticeId = 1;
+    this->Internal->traverseUsingDFS(verticeId);
+    }
+  return this->cycleDetected();
+}
+
+//----------------------------------------------------------------------------
+bool ctkDependencyGraph::cycleDetected()
+{
+  return this->Internal->CycleDetected;
+}
+
+//----------------------------------------------------------------------------
+int ctkDependencyGraph::cycleOrigin()
+{
+  return this->Internal->CycleOrigin;
+}
+
+//----------------------------------------------------------------------------
+int ctkDependencyGraph::cycleEnd()
+{
+  return this->Internal->CycleEnd;
+}
+
+//----------------------------------------------------------------------------
+void ctkDependencyGraph::insertEdge(int from, int to)
+{
+  Q_ASSERT(from > 0 && from <= this->Internal->NVertices);
+  Q_ASSERT(to > 0 && to <= this->Internal->NVertices);
+  
+  // resize if needed
+  int capacity = this->Internal->Edges[from]->capacity(); 
+  if (this->Internal->Degree[from] > capacity)
+    {
+    this->Internal->Edges[from]->resize(capacity + capacity * 0.3);
+    }
+
+  this->Internal->setEdge(from, this->Internal->Degree[from], to);
+  this->Internal->Degree[from]++;
+
+  this->Internal->NEdges++;
+}
+
+//----------------------------------------------------------------------------
+void ctkDependencyGraph::findPath(int from, int to, QList<int>& path)
+{
+	if ((from == to) || (to == -1))
+	  {
+	  path << from;
+		}
+	else 
+	  {
+		this->findPath(from, this->Internal->Parent[to], path);
+		path << to;
+	  }
+}
+
+//----------------------------------------------------------------------------
+bool ctkDependencyGraph::topologicalSort(QList<int>& sorted)
+{
+	QVarLengthArray<int, MAXV> indegree; // indegree of each vertex
+	QQueue<int> zeroin;	  // vertices of indegree 0
+	int x, y;			        // current and next vertex
+  
+  indegree.resize(this->Internal->NVertices + 1);
+	
+	// resize if needed
+	if (this->Internal->NVertices > MAXV)
+	  {
+	  indegree.resize(this->Internal->NVertices);
+	  }
+
+	this->Internal->computeIndegrees(indegree);
+	
+	for (int i=1; i <= this->Internal->NVertices; i++)
+	  {
+		if (indegree[i] == 0) 
+		  {
+		  zeroin.enqueue(i);
+		  }
+		}
+
+	int j=0;
+	while (zeroin.empty() == false) 
+	  {
+		j = j+1;
+		x = zeroin.dequeue();
+		sorted << x;
+		for (int i=0; i < this->Internal->Degree[x]; i++)
+		  {
+			y = this->Internal->edge(x, i);
+			indegree[y] --;
+			if (indegree[y] == 0)
+			  {
+			  zeroin.enqueue(y);
+			  }
+		  }
+	  }
+
+	if (j != this->Internal->NVertices)
+	  {
+		return false;
+		}
+		
+  return true;
+}

+ 67 - 0
Libs/Core/ctkDependencyGraph.h

@@ -0,0 +1,67 @@
+#ifndef __ctkDependencyGraph_h
+#define __ctkDependencyGraph_h
+
+// QT includes
+#include <QString>
+
+class ctkDependencyGraph
+{
+public:
+  ctkDependencyGraph(int nvertices, int nedges);
+  ~ctkDependencyGraph();
+  
+  void printAdditionalInfo();
+  void printGraph();
+  
+  /// Get the number of vertices associated with current graph
+  int numberOfVertices();
+  
+  /// Get the number of edges associated with current graph
+  int numberOfEdges();
+  
+  /// Traverse graph and check for cycle
+  bool checkForCycle();
+  
+  /// Return true if there is at least one cycle
+  bool cycleDetected();
+  
+  /// If a cycle has been detected, return the origin of the cycle otherwise 0.
+  int cycleOrigin();
+  
+  /// If a cycle has been detected, return the end of the cycle otherwise 0.
+  int cycleEnd();
+  
+  // The traverse of the tree will print information on standard output
+  void setVerbose(bool verbose);
+  
+  /// Insert edge
+  /// (from, to) indicate a relation between two vertices
+  /// Note also that vertex id should be >= 1
+  void insertEdge(int from, int to);
+  
+  /// Retrieve the path between two vertices
+  void findPath(int from, int to, QList<int>& path);
+  
+  /// List of edge to exclude
+  /// An edge is specified using its extremity
+  void setEdgeListToExclude(const QList<int>& list);
+  
+  /// The default implementation check if 'edge' is in the list of edge to exclude
+  /// See setEdgeListToExclude
+  virtual bool shouldExcludeEdge(int edge);
+  
+  /// Called each time an edge is visited
+  virtual void processEdge(int /*from*/, int /*to*/){}
+  
+  /// Perform a topological search
+  /// Return false if the graph contains cycles
+  /// See cycleDetected, cycleOrigin, cycleEnd
+  bool topologicalSort(QList<int>& sorted);
+  
+private:
+  class ctkInternal; 
+  ctkInternal* Internal;
+};
+
+#endif
+