Browse Source

Support for isolated vertices in dependency graphs.

In the current default configuration of CTK, CTKCore and CTKPluginFramework
are built by default. Both have no dependencies on other CTK libraries,
so the generated DGraphInput.txt file was empty. Therefore, the CMake
variable CTEST_PROJECT_SUBPROJECTS was empty and was set to CTKCore only.
Hence the ctkDashboardScript.cmake file only built CTKCore.

This commit extends DGraph to also write isolated vertices to the output
file and the global topological sort on the resulting graph will contain
these isolated vertices.
Sascha Zelzer 14 years ago
parent
commit
cee81fecc4

+ 22 - 0
CMake/ctkFunctionGenerateDGraphInput.cmake

@@ -36,6 +36,7 @@ FUNCTION(ctkFunctionGenerateDGraphInput dir target_directories)
 
   SET(edges)
   SET(vertices)
+  SET(isolated_vertex_candidates)
   
   FOREACH(target_info ${target_directories})
 
@@ -87,6 +88,9 @@ FUNCTION(ctkFunctionGenerateDGraphInput dir target_directories)
 
       IF(ctk_dependencies)
         LIST(APPEND vertices ${target_project_name})
+      ELSE()
+        # isolated vertex candidate
+        LIST(APPEND isolated_vertex_candidates ${target_project_name})
       ENDIF()
 
       # Generate XML related to the dependencies
@@ -100,6 +104,24 @@ FUNCTION(ctkFunctionGenerateDGraphInput dir target_directories)
     
   ENDFOREACH()
 
+  FOREACH(isolated_vertex_candidate ${isolated_vertex_candidates})
+    SET(_found 0)
+    FOREACH(dgraph_entry ${dgraph_list})
+      STRING(REPLACE "\n" "" dgraph_entry "${dgraph_entry}")
+      STRING(REPLACE " " ";" dgraph_entry "${dgraph_entry}")
+      LIST(FIND dgraph_entry ${isolated_vertex_candidate} _index)
+      IF(_index GREATER -1)
+        SET(_found 1)
+        BREAK()
+      ENDIF()
+    ENDFOREACH()
+
+    IF(NOT _found)
+      LIST(APPEND vertices ${isolated_vertex_candidate})
+      SET(dgraph_list "${isolated_vertex_candidate}\n" ${dgraph_list})
+    ENDIF()
+  ENDFOREACH()
+
   IF(vertices)
     LIST(REMOVE_DUPLICATES vertices)
   ENDIF()

+ 191 - 0
Libs/Core/Testing/Cpp/ctkDependencyGraphTest2.cpp

@@ -322,5 +322,196 @@ int ctkDependencyGraphTest2(int argc, char * argv [] )
 
   }
 
+  // check that topological ordering and paths
+  // work on disconnected graphs and isolated vertices
+  {
+  const int numberOfVertices = 13;
+
+  ctkDependencyGraph graph(numberOfVertices);
+
+  /* 1 -> 2  -> 3
+   *       \
+   *         -> 4
+   *
+   *         ->  7 ->
+   *       /          \
+   * 5 -> 6  ->  8 ->  9
+   *             ^
+   *             |
+   *            10 -> 11
+   *
+   * 12   13
+   */
+  graph.insertEdge(1,2);
+  graph.insertEdge(2,3);
+  graph.insertEdge(2,4);
+  graph.insertEdge(5,6);
+  graph.insertEdge(6,7);
+  graph.insertEdge(6,8);
+  graph.insertEdge(7,9);
+  graph.insertEdge(8,9);
+  graph.insertEdge(10,8);
+  graph.insertEdge(10,11);
+
+  int expectedNumberOfEdge = 10;
+
+  int nov = graph.numberOfVertices();
+
+  if( nov != numberOfVertices )
+    {
+    qCritical() << "Number of vertices does not match (expected" << numberOfVertices << "got" << nov << ")";
+    return EXIT_FAILURE;
+    }
+
+  int noe = graph.numberOfEdges();
+  if( noe != expectedNumberOfEdge )
+    {
+    qCritical() << "Number of edges does not match (expected" << expectedNumberOfEdge << "got" << noe << ")";
+    return EXIT_FAILURE;
+    }
+
+  bool cfc = graph.checkForCycle();
+
+  if( cfc == true )
+    {
+    qCritical() << "Cycle detection failed";
+    return EXIT_FAILURE;
+    }
+
+  bool cdtd = graph.cycleDetected();
+
+  if( cdtd == true )
+    {
+    qCritical() << "Cycle detected flag wrong";
+    return EXIT_FAILURE;
+    }
+
+  QList<int> sources;
+  graph.sourceVertices(sources);
+
+  QList<int> expectedSources;
+  expectedSources << 1 << 5 << 10 << 12 << 13;
+
+  if (sources != expectedSources)
+    {
+    qCritical() << "Source vertices do not match (expected" << expectedSources << "got" << sources << ")";
+    return EXIT_FAILURE;
+    }
+
+  QList<int> globalSort;
+  graph.topologicalSort(globalSort);
+
+  QList<int> expectedGlobalSort;
+  expectedGlobalSort << 1 << 5 << 10 << 12 << 13 << 2 << 6 << 11 << 3 << 4 << 7 << 8 << 9;
+  if (globalSort != expectedGlobalSort)
+  {
+    qCritical() << "Topological sort error (expected" << expectedGlobalSort << "got" << globalSort << ")";
+    return EXIT_FAILURE;
+  }
+
+  QList<int> subSort10;
+  graph.topologicalSort(subSort10, 10);
+
+  QList<int> expectedSubSort10;
+  expectedSubSort10 << 10 << 8 << 11 << 9;
+  if (subSort10 != expectedSubSort10)
+  {
+    qCritical() << "Topological subgraph sort error (expected" << expectedSubSort10 << "got" << subSort10 << ")";
+    return EXIT_FAILURE;
+  }
+
+  QList<int> subSort12;
+  graph.topologicalSort(subSort12, 12);
+
+  QList<int> expectedSubSort12;
+  expectedSubSort12 << 12;
+  if (subSort12 != expectedSubSort12)
+  {
+    qCritical() << "Topological subgraph sort error (expected" << expectedSubSort12 << "got" << subSort12 << ")";
+    return EXIT_FAILURE;
+  }
+
+  }
+
+  // check that topological ordering and paths
+  // work on a null graph
+  {
+  const int numberOfVertices = 3;
+
+  ctkDependencyGraph graph(numberOfVertices);
+
+  /*
+   * 1  2  3
+   */
+  // a null graph has no edges
+  int expectedNumberOfEdge = 0;
+
+  int nov = graph.numberOfVertices();
+
+  if( nov != numberOfVertices )
+    {
+    qCritical() << "Number of vertices does not match (expected" << numberOfVertices << "got" << nov << ")";
+    return EXIT_FAILURE;
+    }
+
+  int noe = graph.numberOfEdges();
+  if( noe != expectedNumberOfEdge )
+    {
+    qCritical() << "Number of edges does not match (expected" << expectedNumberOfEdge << "got" << noe << ")";
+    return EXIT_FAILURE;
+    }
+
+  bool cfc = graph.checkForCycle();
+
+  if( cfc == true )
+    {
+    qCritical() << "Cycle detection failed";
+    return EXIT_FAILURE;
+    }
+
+  bool cdtd = graph.cycleDetected();
+
+  if( cdtd == true )
+    {
+    qCritical() << "Cycle detected flag wrong";
+    return EXIT_FAILURE;
+    }
+
+  QList<int> sources;
+  graph.sourceVertices(sources);
+
+  QList<int> expectedSources;
+  expectedSources << 1 << 2 << 3;
+
+  if (sources != expectedSources)
+    {
+    qCritical() << "Source vertices do not match (expected" << expectedSources << "got" << sources << ")";
+    return EXIT_FAILURE;
+    }
+
+  QList<int> globalSort;
+  graph.topologicalSort(globalSort);
+
+  QList<int> expectedGlobalSort;
+  expectedGlobalSort << 1 << 2 << 3;
+  if (globalSort != expectedGlobalSort)
+  {
+    qCritical() << "Topological sort error (expected" << expectedGlobalSort << "got" << globalSort << ")";
+    return EXIT_FAILURE;
+  }
+
+  QList<int> subSort2;
+  graph.topologicalSort(subSort2, 2);
+
+  QList<int> expectedSubSort2;
+  expectedSubSort2 << 2;
+  if (subSort2 != expectedSubSort2)
+  {
+    qCritical() << "Topological subgraph sort error (expected" << expectedSubSort2 << "got" << subSort2 << ")";
+    return EXIT_FAILURE;
+  }
+
+  }
+
   return EXIT_SUCCESS;
 }

+ 20 - 7
Utilities/DGraph/DGraph.cpp

@@ -166,7 +166,7 @@ int main(int argc, char** argv)
   QHash<QString, int> vertexLabelToId;
   
   // Regular expression to extract two label
-  QRegExp twolabel_re("^(.+)\\s+(.+)");
+  QRegExp twolabel_re("^\\s*(\\S+)\\s*$|^\\s*(\\S+)\\s*(\\S+)\\s*$");
   
   // Read vertex connection
   int lineNumber = 2;
@@ -185,20 +185,33 @@ int main(int argc, char** argv)
     int pos = twolabel_re.indexIn(line.trimmed());
     if (pos != 0)
       {
-      displayError(argv[0], QString("Error in file '%1' - line:%2 - Expected format is: <label> <label>")
+      displayError(argv[0], QString("Error in file '%1' - line:%2 - Expected format is: <label> [<label>]")
         .arg(filepath).arg(lineNumber));
       return EXIT_FAILURE;
       }
     lineNumber++;
 
     QStringList list = twolabel_re.capturedTexts();
-    Q_ASSERT(list.size() == 3);
+    Q_ASSERT(list.size() == 4);
 
-    int from = getOrGenerateId(vertexIdToLabel, vertexLabelToId, list[1]);
-    int to = getOrGenerateId(vertexIdToLabel, vertexLabelToId, list[2]);
+    int from = -1;
+    int to = -1;
+    if (list[1].isEmpty())
+      {
+      from = getOrGenerateId(vertexIdToLabel, vertexLabelToId, list[2]);
+      to = getOrGenerateId(vertexIdToLabel, vertexLabelToId, list[3]);
+      }
 
-    // Insert edge
-    mygraph.insertEdge(from, to);
+    if (to > -1)
+      {
+      // Insert edge if we got two vertices
+      mygraph.insertEdge(from, to);
+      }
+    else
+      {
+      // Just generate an entry in the vertexIdToLabel map
+      getOrGenerateId(vertexIdToLabel, vertexLabelToId, list[1]);
+      }
 
     line = in.readLine();
     }