浏览代码

ENH: In DGraph, change '-path' into '-paths'.

ctkDependencyGraph now propose the method findPaths(from, to, paths) allowing
to retrieve all paths allowing to go FROM a child TO a parent.

Corresponding tests have been added.
Jean-Christophe Fillion-Robin 15 年之前
父节点
当前提交
2ecca6c93e

+ 18 - 15
CMake/ctkMacroValidateBuildOptions.cmake

@@ -94,10 +94,10 @@ MACRO(ctkMacroValidateBuildOptions dir executable target_directories)
     IF(${${option_name}})
       # Obtain dependency path
       EXECUTE_PROCESS(
-        COMMAND "${executable}" "${CTK_BINARY_DIR}/DGraphInput-alldep.txt" -path ${target_project_name}
+        COMMAND "${executable}" "${CTK_BINARY_DIR}/DGraphInput-alldep.txt" -paths ${target_project_name}
         WORKING_DIRECTORY ${CTK_BINARY_DIR}
         RESULT_VARIABLE RESULT_VAR
-        OUTPUT_VARIABLE dep_path
+        OUTPUT_VARIABLE dep_paths
         ERROR_VARIABLE error
         OUTPUT_STRIP_TRAILING_WHITESPACE
         )
@@ -105,20 +105,23 @@ MACRO(ctkMacroValidateBuildOptions dir executable target_directories)
         MESSAGE(FATAL_ERROR "Failed to obtain dependence path of ${subir}.\n${RESULT_VAR}\n${CTK_BINARY_DIR}\n${error}")
       ENDIF()
 
-      # Convert 'dep_path' to a list
-      STRING(REPLACE " " "\\;" dep_path_list ${dep_path})
-      SET(dep_path_list ${dep_path_list})
+      FOREACH(dep_path ${dep_paths})
 
-      #MESSAGE("path for ${target_project_name} is: ${dep_path}")
-      
-      # Check if all target included in the dependency path are enabled
-      FOREACH(dep ${dep_path_list})
-        ctkMacroGetOptionName("${target_directories_with_target_name}" ${dep} dep_option)
-        IF(NOT ${${dep_option}})
-          # Enable option
-          MESSAGE(STATUS "Enabling option [${dep_option}] required by target [${target_project_name}]")
-          SET(${dep_option} ON CACHE BOOL "Enable ${target_project_name} library" FORCE)
-        ENDIF()
+        # Convert 'dep_path' to a list
+        STRING(REPLACE " " "\\;" dep_path_list ${dep_path})
+        SET(dep_path_list ${dep_path_list})
+
+        #MESSAGE("path for ${target_project_name} is: ${dep_path}")
+        
+        # Check if all target included in the dependency path are enabled
+        FOREACH(dep ${dep_path_list})
+          ctkMacroGetOptionName("${target_directories_with_target_name}" ${dep} dep_option)
+          IF(NOT ${${dep_option}})
+            # Enable option
+            MESSAGE(STATUS "Enabling option [${dep_option}] required by [${target_project_name}]")
+            SET(${dep_option} ON CACHE BOOL "Enable ${target_project_name} library" FORCE)
+          ENDIF()
+        ENDFOREACH()
       ENDFOREACH()
     ENDIF()
     

+ 143 - 16
Libs/Core/Testing/Cpp/ctkDependencyGraphTest1.cpp

@@ -16,27 +16,66 @@
 #include "ctkDependencyGraph.h"
 
 // STL includes
-#include <stdlib.h>
+#include <cstdlib>
 #include <iostream>
 
+namespace
+{
+void printIntegerList(const char* msg, const QList<int>& list, bool endl = true)
+{
+  std::cerr << msg; 
+  foreach(int l, list)
+    {
+    std::cerr << l << " "; 
+    }
+  if (endl)
+    {
+    std::cerr << std::endl;
+    }
+}
+}
+
 //-----------------------------------------------------------------------------
 int ctkDependencyGraphTest1(int argc, char * argv [] )
 {
   Q_UNUSED(argc);
   Q_UNUSED(argv);  
 
-  const int numberOfVertices = 4;
-  const int numberOfEdges = 3;
+  const int numberOfVertices = 14;
 
-  ctkDependencyGraph  graph(numberOfVertices,numberOfEdges);
+  ctkDependencyGraph  graph(numberOfVertices);
 
   graph.setVerbose(true);
   graph.setVerbose(false);
-  graph.setVerbose(true);
-
-  graph.insertEdge(1,2);
-  graph.insertEdge(2,3);
+  //graph.setVerbose(true);
+
+  //
+  // 6 - 8
+  // |
+  // 7 - 5 - 1 - 2 - 
+  // |       |     | - 9
+  // |       4 - 3 -   |
+  // |                 |
+  // 10 - 11 - 12 - 13 - 14
+  //
   graph.insertEdge(3,4);
+  graph.insertEdge(4,1);
+  graph.insertEdge(1,5);
+  graph.insertEdge(5,7);
+  graph.insertEdge(2,1);
+  graph.insertEdge(8,6);
+  graph.insertEdge(6,7);
+  graph.insertEdge(9,2);
+  graph.insertEdge(9,3);
+  graph.insertEdge(14,9);
+  graph.insertEdge(14,13);
+  graph.insertEdge(13,12);
+  graph.insertEdge(12,11);
+  graph.insertEdge(11,10);
+  graph.insertEdge(10,7);
+
+  int expectedNumberOfEdge = 15;
+  
 
   graph.printAdditionalInfo();
   graph.printGraph();
@@ -49,8 +88,7 @@ int ctkDependencyGraphTest1(int argc, char * argv [] )
     }
 
   int noe = graph.numberOfEdges();
-
-  if( noe != numberOfEdges + 3 )
+  if( noe != expectedNumberOfEdge )
     {
     return EXIT_FAILURE;
     }
@@ -74,16 +112,105 @@ int ctkDependencyGraphTest1(int argc, char * argv [] )
   int cend = graph.cycleEnd();
 
   QList<int> path;
+  QList<int> expectedPath;
+
+  graph.findPath( 8, 7, path );
+  expectedPath << 8 << 6 << 7;
+  if (path != expectedPath)
+    {
+    std::cerr << "Problem with findPath()" << std::endl;
+    printIntegerList("current:", path);
+    printIntegerList("expected:", expectedPath);
+    return EXIT_FAILURE;
+    }
+
+  path.clear();
+  expectedPath.clear();
+  
+  graph.findPath( 1, 7, path );
+  expectedPath << 1 << 5 << 7;
+  if (path != expectedPath)
+    {
+    std::cerr << "Problem with findPath()" << std::endl;
+    printIntegerList("current:", path);
+    printIntegerList("expected:", expectedPath);
+    return EXIT_FAILURE;
+    }
 
-  graph.findPath( 1, 4, path );
+  path.clear();
+  expectedPath.clear();
+  
+  graph.findPath( 3, 7, path );
+  expectedPath << 3 << 4 << 1 << 5 << 7;
+  if (path != expectedPath)
+    {
+    std::cerr << "Problem with findPath()" << std::endl;
+    printIntegerList("current:", path);
+    printIntegerList("expected:", expectedPath);
+    return EXIT_FAILURE;
+    }
 
-  QList<int> list;
-  graph.setEdgeListToExclude( list );
+  path.clear();
+  expectedPath.clear();
+  
+  graph.findPath( 2, 5, path );
+  expectedPath << 2 << 1 << 5;
+  if (path != expectedPath)
+    {
+    std::cerr << "Problem with findPath()" << std::endl;
+    printIntegerList("current:", path);
+    printIntegerList("expected:", expectedPath);
+    return EXIT_FAILURE;
+    }
+
+  path.clear();
+  expectedPath.clear();
+
+  QList<QList<int>* > paths;
+  QList<int> expectedPath1;
+  QList<int> expectedPath2;
+  QList<int> expectedPath3;
 
-  graph.shouldExcludeEdge(2);
+  graph.findPaths(14, 5, paths);
+  expectedPath1 << 14 << 9 << 3 << 4 << 1 << 5;
+  expectedPath2 << 14 << 9 << 2 << 1 << 5;
+  foreach(QList<int>* p, paths)
+    {
+    if (*p != expectedPath1 && *p != expectedPath2)
+      {
+      printIntegerList("current:", *p);
+      printIntegerList("expected:", expectedPath1, false);
+      printIntegerList(" or ", expectedPath2);
+      return EXIT_FAILURE;
+      }
+    }
+
+  expectedPath1.clear();
+  expectedPath2.clear();
+
+  graph.findPaths(14, 7, paths);
+  expectedPath1 << 14 << 9 << 3 << 4 << 1 << 5 << 7;
+  expectedPath2 << 14 << 9 << 2 << 1 << 5 << 7;
+  expectedPath3 << 14 << 13 << 12 << 11 << 10 << 7;
+  foreach(QList<int>* p, paths)
+    {
+    if (*p != expectedPath1 && *p != expectedPath2 && *p != expectedPath3)
+      {
+      printIntegerList("current:", *p);
+      printIntegerList("expected:", expectedPath1, false);
+      printIntegerList(" or ", expectedPath2, false);
+      printIntegerList(" or ", expectedPath3);
+      return EXIT_FAILURE;
+      }
+    }
 
-  QList<int> sortedlist;
-  graph.topologicalSort( sortedlist );
+//   QList<int> list;
+//   graph.setEdgeListToExclude( list );
+// 
+//   graph.shouldExcludeEdge(2);
+// 
+//   QList<int> sortedlist;
+//   graph.topologicalSort( sortedlist );
 
   return EXIT_SUCCESS;
 }

+ 97 - 16
Libs/Core/ctkDependencyGraph.cpp

@@ -43,6 +43,12 @@ public:
   
   /// Called each time a vertex is processed
   void processVertex(int v);
+
+  /// Retrieve the path between two vertices
+  void findPathDFS(int from, int to, QList<int>& path);
+
+  /// Recursive function used by findPaths to retrieve the path between two vertices
+  void findPathsRec(int from, int to, QList<int>* path, QList<QList<int>* >& paths);
   
   void setEdge(int vertice, int degree, int value);
   int edge(int vertice, int degree);
@@ -156,11 +162,11 @@ void ctkDependencyGraph::ctkInternal::processEdge(int from, int to)
     if (this->Verbose)
       {
       QList<int> path;
-      this->P->findPath(from, to, path);
+      this->findPathDFS(from, to, path);
       qWarning() << "Cycle detected from " << to << " to " << from;
       qWarning() << " " << path;
       path.clear();
-      this->P->findPath(to, from, path);
+      this->findPathDFS(to, from, path);
       qWarning() << " " << path;
       }
     this->Abort = true;
@@ -191,17 +197,61 @@ int ctkDependencyGraph::ctkInternal::edge(int vertice, int degree)
   Q_ASSERT(degree < MAXDEGREE);
   return (*this->Edges[vertice])[degree];
 }
+
+//----------------------------------------------------------------------------
+void ctkDependencyGraph::ctkInternal::findPathDFS(int from, int to, QList<int>& path)
+{
+  if ((from == to) || (to == -1))
+    {
+    path << from;
+    }
+  else 
+    {
+    this->findPathDFS(from, this->Parent[to], path);
+    path << to;
+    }
+}
+
+//----------------------------------------------------------------------------
+void ctkDependencyGraph::ctkInternal::findPathsRec(
+  int from, int to, QList<int>* path, QList<QList<int>* >& paths)
+{
+  if (from == to)
+    {
+    return;
+    }
+  
+  QList<int> branch(*path);
+  int child = from;
+  for (int j=0; j < this->Degree[child]; j++)
+    {
+    if (j == 0)
+      {
+      int parent = this->edge(child, j);
+      *path << parent;
+      this->findPathsRec(parent, to, path, paths);
+      }
+    else
+      {
+      int parent = this->edge(child, j);
+      // Copy path and add it to the list
+      QList<int>* pathCopy = new QList<int>(branch);
+      paths << pathCopy;
+      *pathCopy << parent;
+      this->findPathsRec(parent, to, pathCopy, paths);
+      }
+    }
+}
     
 //----------------------------------------------------------------------------
 // ctkDependencyGraph methods
 
 //----------------------------------------------------------------------------
-ctkDependencyGraph::ctkDependencyGraph(int nvertices, int nedges)
+ctkDependencyGraph::ctkDependencyGraph(int nvertices)
 {
   this->Internal = new ctkInternal(this);
   
   this->Internal->NVertices = nvertices; 
-  this->Internal->NEdges = nedges;
   
   // Resize internal array
   this->Internal->Processed.resize(nvertices + 1);
@@ -254,17 +304,17 @@ void ctkDependencyGraph::printAdditionalInfo()
   qDebug() << " [Processed]";
   for(int i=1; i < this->Internal->Processed.size(); i++)
     {
-    qDebug() << i << "->" << this->Internal->Processed[i]; 
+    qDebug() << i << "->" << this->Internal->Processed[i];
     }
   qDebug() << " [Discovered]";
   for(int i=1; i < this->Internal->Discovered.size(); i++)
     {
-    qDebug() << i << "->" << this->Internal->Discovered[i]; 
+    qDebug() << i << "->" << this->Internal->Discovered[i];
     }
   qDebug() << " [Parent]";
   for(int i=1; i < this->Internal->Parent.size(); i++)
     {
-    qDebug() << i << "->" << this->Internal->Parent[i]; 
+    qDebug() << i << "->" << this->Internal->Parent[i];
     }
   qDebug() << " [Graph]"; 
   this->printGraph();
@@ -364,17 +414,48 @@ void ctkDependencyGraph::insertEdge(int from, int to)
 }
 
 //----------------------------------------------------------------------------
+void ctkDependencyGraph::findPaths(int from, int to, QList<QList<int>* >& paths)
+{
+  QList<int>* path = new QList<int>;
+  *path << from; 
+  paths << path;
+  this->Internal->findPathsRec(from, to, path, paths);
+
+  QList<int> pathToRemove;
+  // Remove list no ending with the requested element
+  int i = 0; 
+  while (!paths.isEmpty() && i < paths.size())
+    {
+    QList<int>* p = paths[i];
+    Q_ASSERT(p);
+    if (p->last() != to)
+      {
+      paths.removeAt(i);
+      delete p; 
+      }
+    else
+      {
+      i++;
+      }
+    }
+}
+
+//----------------------------------------------------------------------------
 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;
-	  }
+  int child = from;
+  int parent = this->Internal->edge(child, 0);
+  path << child; 
+  while (parent > 0)
+    {
+    path << parent;
+    if (parent == to)
+      {
+      break;
+      }
+    child = parent;
+    parent = this->Internal->edge(child, 0);
+    }
 }
 
 //----------------------------------------------------------------------------

+ 5 - 1
Libs/Core/ctkDependencyGraph.h

@@ -25,7 +25,7 @@
 class CTK_CORE_EXPORT ctkDependencyGraph
 {
 public:
-  ctkDependencyGraph(int nvertices, int nedges);
+  ctkDependencyGraph(int nvertices);
   ~ctkDependencyGraph();
   
   void printAdditionalInfo();
@@ -56,6 +56,10 @@ public:
   /// (from, to) indicate a relation between two vertices
   /// Note also that vertex id should be >= 1
   void insertEdge(int from, int to);
+
+  /// Retrieve the paths between two vertices
+  /// Caller is responsible to clear paths list
+  void findPaths(int from, int to, QList<QList<int>* >& paths);
   
   /// Retrieve the path between two vertices
   void findPath(int from, int to, QList<int>& path);

+ 22 - 11
Utilities/DGraph/DGraph.cpp

@@ -16,7 +16,7 @@
 //----------------------------------------------------------------------------
 QString help(const QString& progName)
 {
-  QString msg = "Usage: %1 <graphfile> [-path Label]";
+  QString msg = "Usage: %1 <graphfile> [-paths Label]";
   return msg.arg(progName);
 }
 
@@ -70,7 +70,7 @@ int main(int argc, char** argv)
     }
   if (argc == 4)
     {
-    if (QString(argv[2]).compare("-path")!=0)
+    if (QString(argv[2]).compare("-paths")!=0)
       {
       displayError(argv[0], QString("Wrong argument: %1").arg(argv[2]));
       return EXIT_FAILURE;
@@ -129,7 +129,7 @@ int main(int argc, char** argv)
     }
 
   // Init
-  ctkDependencyGraph mygraph(numberOfVertices, numberOfEdges);
+  ctkDependencyGraph mygraph(numberOfVertices);
   mygraph.setVerbose(verbose);
 
   // Map between vertex label and vertex id
@@ -174,6 +174,8 @@ int main(int argc, char** argv)
     }
   while (!line.isNull());
 
+  Q_ASSERT(numberOfEdges == mygraph.numberOfEdges());
+
   if (verbose)
     {
     mygraph.printGraph();
@@ -241,18 +243,27 @@ int main(int argc, char** argv)
     QList<int> out;
     if (mygraph.topologicalSort(out))
       {
-      // Assume all target depends on the first lib
+      // Assume all targets depend on the first lib
       int rootId = out.last();
       int labelId = vertexLabelToId[label];
-      QList<int> path;
-      mygraph.findPath(labelId, rootId, path);
-      for(int i=0; i<path.size(); i++)
+      QList<QList<int>*> paths;
+      mygraph.findPaths(labelId, rootId, paths);
+      for(int i=0; i < paths.size(); i++)
         {
-        int id = path[i];
-        std::cout << vertexIdToLabel[id].toStdString();
-        if (i != path.size() - 1)
+        QList<int>* p = paths[i];
+        Q_ASSERT(p);
+        for(int j=0; j < p->size(); j++)
           {
-          std::cout << " ";
+          int id = p->at(j);
+          std::cout << vertexIdToLabel[id].toStdString();
+          if (j != p->size() - 1)
+            {
+            std::cout << " ";
+            }
+          }
+        if (i != paths.size() - 1)
+          {
+          std::cout << ";";
           }
         }
       }