Commit 762f3d54 authored by Kristin Berry's avatar Kristin Berry
Browse files

Removed unused MinimumSpanningTree code from ControlNetwork

parent f4a8589e
Loading
Loading
Loading
Loading
+0 −161
Original line number Diff line number Diff line
@@ -860,167 +860,6 @@ namespace Isis {
  }


  /**
   * This method uses Kruskal's Algorithm to construct a minimum spanning tree
   * of the given island, with control measures acting as the edges between
   * graph nodes.   Because measures do not directly connect graph nodes, but
   * rather connect graph nodes to control points, points are considered
   * "intermediate vertices".  When building the tree, we treat points like
   * normal graph node vertices, but after the tree is built, we prune away any
   * measures that, in conjunction with a point, form an "incomplete edge".
   * Such an edge goes from a graph node to a point, but does not have another
   * edge going from that point to another node.  Since the primary purpose of
   * this tree is to evaluate image connectivity, such edges are unnecessary.
   * A "complete edge" consists of two measures and a point, and connects two
   * graph nodes, or images.
   *
   * The cost of each edge is determined by the provided less-than comparison
   * function.  If a Control Measure is less-than another, it is said to have a
   * lower cost, and is thus more likely to appear in the minimum spanning tree.
   *
   * Minimum spanning trees are constructed on islands, not networks, so it is
   * important that the caller first retrieve the list of islands in the network
   * from the GetNodeConnections() method before invoking this method.  Creating
   * a minimum spanning tree for an entire network with multiple islands would
   * be impractical, as this method does not account for the possibility that
   * some nodes are disconnected in the input.
   *
   * A common usage of the minimum spanning tree is to measure the importance of
   * a given measure or point to the overall connectivity of a network.  If a
   * measurement is not in the MST, we do not need to worry about creating
   * additional islands by removing it.
   *
   * It is important that the user choose their less-than function carefully.
   * If a poor less-than function is used, then "bad" measures could end up in
   * the MST while "good" measures are excluded, thus giving the user the false
   * impression that a good measure can be safely deleted, while a bad measure
   * must be preserved.  The application "cnetwinnow", for example, tries to
   * remove as many measures with high residuals as possible without increasing
   * the island count of the network, so its less-than function compares the
   * residual magnitude of the two input measures.
   *
   * @param island The list of graph nodes forming the island to be minimized
   * @param lessThan A comparison function telling us if one measure is better
   *                 than another
   *
   * @return The set of all measures (edges) in the minimum spanning tree
   */
  QSet< ControlMeasure * > ControlNet::MinimumSpanningTree(
      QList< ControlCubeGraphNode *> &island,
      bool lessThan(const ControlMeasure *, const ControlMeasure *)) const {

    // Kruskal's Algorithm
    QSet< ControlMeasure * > minimumTree;

    // We start with a map containing all the unconnected vertices (nodes and
    // points), each its own single-element tree.  Our goal is join them all
    // together into one tree connecting every vertex.  We map into the forest
    // with point ID and serial number so the two types of vertices can share a
    // common, nearly constant-time lookup interface.
    QMap< QString, ControlVertex * > forest;

    // Get a list of all the candidate edges on the island, and a set of their
    // associated Control Points (to avoid duplication, as measures share common
    // points).  Keep a count of how many measures in the MST are connected to
    // each point, as we'll want to prune off points with only one such
    // conenction.
    QList< ControlMeasure * > edges;
    QMap< ControlPoint *, int > uniquePoints;
    for (int i = 0; i < island.size(); i++) {
      // Add each graph node as a tree in the forest
      ControlCubeGraphNode *node = island[i];
      forest.insert(node->getSerialNumber(), new ControlVertex(node));

      // Every graph node has a list of measures: these are our edges
      QList< ControlMeasure * > measures = node->getMeasures();
      for (int j = 0; j < measures.size(); j++) {
        edges.append(measures[j]);

        // Every measure has a point: these act as intermediate vertices.  We
        // keep a count of how many edges in the MST come off this point.
        // Points with less than 2 edges are considered incomplete, as they do
        // not form a connection from one graph node to another, or a "complete
        // edge"
        uniquePoints.insert(measures[j]->Parent(), 0);
      }
    }

    // Sort the edges in increasing cost with the provided less-than function
    qSort(edges.begin(), edges.end(), lessThan);

    // Add every unique point on the island as a tree in the forest
    QList< ControlPoint * > pointList = uniquePoints.keys();
    for (int i = 0; i < pointList.size(); i++) {
      ControlPoint *point = pointList[i];
      forest.insert(point->GetId(), new ControlVertex(point));
    }

    // Every time we join two trees together, we reduce the total number of
    // trees by one, but our forest data structure is unchanged, so keep a
    // record of the actual forest size to decrement manually as we go along
    int trees = forest.size();

    // Keep trying to join trees until there is only one tree remaining or we're
    // out of edges to try
    while (trees > 1 && edges.size() > 0) {
      // Try to add our lowest-cost edge to the minimum spanning tree
      ControlMeasure *leastEdge = edges.takeFirst();

      // Each edge connects two vertices: a point and a graph node.  So grab the
      // trees for each node and check that they're disjoint, and thus able to
      // be joined.
      QString pointId = leastEdge->Parent()->GetId();
      ControlVertex *pointVertex = forest[pointId];

      QString serialNum = leastEdge->ControlSN()->getSerialNumber();
      ControlVertex *nodeVertex = forest[serialNum];

      // When the edge joins two different trees (i.e., they have different
      // roots), then add the edge to the minimum spanning tree and join the
      // trees into one.  Otherwise, we have formed a cycle and should thus
      // discard the edge.
      if (pointVertex->getRoot() != nodeVertex->getRoot()) {
        ControlVertex::join(pointVertex, nodeVertex);
        trees--;
        minimumTree.insert(leastEdge);
        uniquePoints[pointVertex->getPoint()]++;
      }
    }

    // Prune edges that go from a graph node to a point, but not from that
    // point to another graph node.  We care about image (graph node)
    // connectivity, not point connectivity.  A complete edge consists of two
    // measures and a point, so remove any incomplete edges.
    QList< ControlMeasure * > unprunedEdges = minimumTree.values();
    for (int i = 0; i < unprunedEdges.size(); i++) {
      if (uniquePoints[unprunedEdges[i]->Parent()] < 2)
        // The point this edge is connected to does not go on to another node,
        // so prune it
        minimumTree.remove(unprunedEdges[i]);
    }

    // Clean up our vertices.  This will not delete any of the points, measures,
    // or graph nodes.  All of that is owned by the network.
    QList< ControlVertex * > vertexList = forest.values();
    for (int i = 0; i < vertexList.size(); i++) delete vertexList[i];

    // Sanity check: an island with n > 1 nodes must, by definition, have an MST
    // of e edges such that n <= e <= 2n
    int n = island.size();
    int e = minimumTree.size();
    if (n > 1) {
      if (e < n || e > 2 * n) {
        IString msg = "An island of n = [" + IString(n) +
          "] > 1 nodes must have a minimum spanning tree of e edges such that "
          " n <= e <= 2n, but e = [" + IString(e) + "]";
        throw IException(IException::Programmer, msg, _FILEINFO_);
      }
    }

    return minimumTree;
  }


  /**
   * @returns The total number of edges in the bi-directional graph for images
   */
+4 −71
Original line number Diff line number Diff line
@@ -213,6 +213,9 @@ namespace Isis {
   *                           image. Previously, this had to be done throug the graph.
   *   @history 2018-01-26 Kristin Berry - Added pointAdded() function to eliminate redundant measure
   *                           adds to the control network.
   *  @history 2018-01-26 Kristin Berry - Removed unused methods and associated code:
   *                           MinimumSpanningTree, 
   *                           
   */
  class ControlNet : public QObject {
      Q_OBJECT
@@ -243,9 +246,6 @@ namespace Isis {
      QList< ControlCubeGraphNode * > GetCubeGraphNodes();
      QList< QList< QString > > GetSerialConnections() const;
      QList< QList< ControlCubeGraphNode * > > GetNodeConnections() const;
      QSet< ControlMeasure * > MinimumSpanningTree(
          QList< ControlCubeGraphNode *> &island,
          bool lessThan(const ControlMeasure *, const ControlMeasure *)) const;
      int getEdgeCount() const;
      QString CubeGraphToString() const;
      QList< ControlMeasure * > GetMeasuresInCube(QString serialNumber);
@@ -362,73 +362,6 @@ namespace Isis {
          double(ControlMeasure::*m_accessor)() const;
      };


      /**
       * Encapsulation of a vertex in a minimum spanning tree.  Can be either a
       * Control Point or a Graph Node.  Each vertex is connected to another by
       * a measure.  A vertex without a parent vertex is considered a root node,
       * or the base of its own tree.
       *
       * @author ????-??-?? Unknown
       *
       * @internal
       */
      class ControlVertex {
        public:
          //! Construct a vertex from a Graph Node
          ControlVertex(ControlCubeGraphNode *node) {
            m_node = node;
            m_point = NULL;
            m_parent = NULL;
          }

          //! Construct a vertex from a Control Point
          ControlVertex(ControlPoint *point) {
            m_point = point;
            m_node = NULL;
            m_parent = NULL;
          }

          //! Does not own any of its private data
          ~ControlVertex() {}

          //! Set the parent vertex, removing the root node status.
          void setParent(ControlVertex *v) { m_parent = v; }

          //! Get the root node, or greatest ancestor
          ControlVertex * getRoot() {
            ControlVertex *current = this;
            while (current->getParent() != NULL)
              current = current->getParent();
            return current;
          }

          //! Get the parent node.  A root node has no parent.
          ControlVertex * getParent() { return m_parent; }

          //! Get the node representation of this vertex
          ControlCubeGraphNode * getNode() { return m_node; }

          //! Get the point representation of this vertex
          ControlPoint * getPoint() { return m_point; }

          //! Join two nodes by setting one root to be the other's parent
          static void join(ControlVertex *v1, ControlVertex *v2) {
            v1->getRoot()->setParent(v2->getRoot());
          }

        private:
          //! The possibly non-existant graph node
          ControlCubeGraphNode *m_node;

          //! The possibly non-existant control point
          ControlPoint *m_point;

          //! The possibly non-existant parent vertex
          ControlVertex *m_parent;
      };


    private: // data
      //! hash ControlPoints by ControlPoint Id
      QHash< QString, ControlPoint * > * points;
+66 −61
Original line number Diff line number Diff line
@@ -31,6 +31,8 @@ using namespace Isis;
 * @author ????-??-?? Unknown
 * @internal
 *   @history 2016-05-11 Jeannie Backer - Added tests for setTarget methods.
 *   @history 2018-05-29 Kristin Berry - Commented out (to probably be removed) code related to
 *                                       unused, removed methods. 
 */
bool lessThan(const ControlMeasure *m1, const ControlMeasure *m2) {
  return m1->GetResidualMagnitude() < m2->GetResidualMagnitude();
@@ -148,67 +150,70 @@ void testConnectivity() {
  if (islands[0].contains(m9->ControlSN())) islands.swap(0, 1);
  cout << "  " << "Island Count = " << islands.size() << endl;

  cout << "\nTesting MinimumSpanningTree()\n";
  QList< QSet< ControlMeasure * > > spanningTrees;
  int nodeCount = 0;
  for (int i = 0; i < islands.size(); i++) {
    spanningTrees.append(net.MinimumSpanningTree(islands[i], lessThan));
    nodeCount += islands[i].size();
  }

  cout << "  " << "Tree Count = " << spanningTrees.size() << endl;
  cout << "  " << "Graph Node Count = " << nodeCount << endl;

  QList< QMap< QString, ControlMeasure * > > includedMaps;
  QList< QMap< QString, ControlMeasure * > > excludedMaps;
  for (int i = 0; i < spanningTrees.size(); i++) {
    includedMaps.append(QMap< QString, ControlMeasure * >());
    excludedMaps.append(QMap< QString, ControlMeasure * >());
  }

  int measureCount = 0;
  for (int p = 0; p < net.GetNumPoints(); p++) {
    ControlPoint *point = net.GetPoint(p);
    for (int m = 0; m < point->GetNumMeasures(); m++) {
      ControlMeasure *measure = point->GetMeasure(m);
      measureCount++;
      for (int i = 0; i < spanningTrees.size(); i++) {
        if (islands[i].contains(measure->ControlSN())) {
          if (spanningTrees[i].contains(measure))
            includedMaps[i].insert(measureNames[measure], measure);
          else
            excludedMaps[i].insert(measureNames[measure], measure);
        }
      }
    }
  }
  cout << "  " << "Measure Count = " << measureCount << endl;

  int includedMeasures = 0;
  for (int i = 0; i < spanningTrees.size(); i++) {
    QSet< ControlMeasure * > measures = spanningTrees[i];
    includedMeasures += measures.size();
  }

  if (islands.size() != spanningTrees.size()) {
    cout << "  " << "Island Count == " << islands.size() << " != " <<
      spanningTrees.size() << " == MST Count" << endl;
  }
  else {
    cout << "  " << "Island Count == " << islands.size() <<
      " == MST Count" << endl;

    for (int i = 0; i < spanningTrees.size(); i++) {
      cout << "\n  " << "Minimum Spanning Tree " << i << endl;

      cout << "    " << "Nodes = " << islands[i].size() << endl;
      cout << "    " << "Included Measures = " << includedMaps[i].size() << endl;
      printMeasures(includedMaps[i].values(), measureNames);

      cout << "    " << "Excluded Measures = " << excludedMaps[i].size() << endl;
      printMeasures(excludedMaps[i].values(), measureNames);
    }
  }
  // This region all has to do with testing the (unused) removed MinimumSpanningTree 
  // method. It's left in here commented-out for now in case we would like to test 
  // island functionality here, using similar output, after updating ControlNet to use boost. 
//cout << "\nTesting MinimumSpanningTree()\n";
//QList< QSet< ControlMeasure * > > spanningTrees;
//int nodeCount = 0;
//for (int i = 0; i < islands.size(); i++) {
//  spanningTrees.append(net.MinimumSpanningTree(islands[i], lessThan));
//  nodeCount += islands[i].size();
//}
//
//cout << "  " << "Tree Count = " << spanningTrees.size() << endl;
//cout << "  " << "Graph Node Count = " << nodeCount << endl;
//
//QList< QMap< QString, ControlMeasure * > > includedMaps;
//QList< QMap< QString, ControlMeasure * > > excludedMaps;
//for (int i = 0; i < spanningTrees.size(); i++) {
//  includedMaps.append(QMap< QString, ControlMeasure * >());
//  excludedMaps.append(QMap< QString, ControlMeasure * >());
//}
//
//int measureCount = 0;
//for (int p = 0; p < net.GetNumPoints(); p++) {
//  ControlPoint *point = net.GetPoint(p);
//  for (int m = 0; m < point->GetNumMeasures(); m++) {
//    ControlMeasure *measure = point->GetMeasure(m);
//    measureCount++;
//    for (int i = 0; i < spanningTrees.size(); i++) {
//      if (islands[i].contains(measure->ControlSN())) {
//        if (spanningTrees[i].contains(measure))
//          includedMaps[i].insert(measureNames[measure], measure);
//        else
//          excludedMaps[i].insert(measureNames[measure], measure);
//      }
//    }
//  }
//}
//cout << "  " << "Measure Count = " << measureCount << endl;
//
//int includedMeasures = 0;
//for (int i = 0; i < spanningTrees.size(); i++) {
//  QSet< ControlMeasure * > measures = spanningTrees[i];
//  includedMeasures += measures.size();
//}
//
//if (islands.size() != spanningTrees.size()) {
//  cout << "  " << "Island Count == " << islands.size() << " != " <<
//    spanningTrees.size() << " == MST Count" << endl;
//}
//else {
//  cout << "  " << "Island Count == " << islands.size() <<
//    " == MST Count" << endl;
//
//  for (int i = 0; i < spanningTrees.size(); i++) {
//    cout << "\n  " << "Minimum Spanning Tree " << i << endl;
//
//    cout << "    " << "Nodes = " << islands[i].size() << endl;
//    cout << "    " << "Included Measures = " << includedMaps[i].size() << endl;
//    printMeasures(includedMaps[i].values(), measureNames);
//
//    cout << "    " << "Excluded Measures = " << excludedMaps[i].size() << endl;
//    printMeasures(excludedMaps[i].values(), measureNames);
//  }
//}
}