Program Listing for File subgraphs.cpp

Return to documentation for file (lib/subgraphs.cpp)

#include "AnalysisGraph.hpp"
#include "Node.hpp"
#include "utils.hpp"
#include <boost/range/algorithm/for_each.hpp>
#include <boost/range/adaptor/transformed.hpp>
#include <boost/range/adaptor/filtered.hpp>
#include <range/v3/all.hpp>

using boost::for_each;
using delphi::utils::in;
using fmt::print;

using namespace std;

void AnalysisGraph::get_subgraph(int vert,
                                 unordered_set<int>& vertices_to_keep,
                                 int cutoff,
                                 bool inward) {

  // Mark the current vertex visited
  (*this)[vert].visited = true;
  vertices_to_keep.insert(vert);

  if (cutoff != 0) {
    cutoff--;

    // Recursively process all the vertices adjacent to the current vertex
    if (inward) {
      for_each(this->predecessors(vert), [&](int v) {
        if (!(*this)[v].visited) {
          this->get_subgraph(v, vertices_to_keep, cutoff, inward);
        }
      });
    }
    else {
      for_each(this->successors(vert), [&](int v) {
        if (!(*this)[v].visited) {
          this->get_subgraph(v, vertices_to_keep, cutoff, inward);
        }
      });
    }
  }

  // Mark the current vertex unvisited
  (*this)[vert].visited = false;
};

void AnalysisGraph::get_subgraph_between(int start,
                                         int end,
                                         vector<int>& path,
                                         unordered_set<int>& vertices_to_keep,
                                         int cutoff) {

  // Mark the current vertex visited
  (*this)[start].visited = true;

  // Add this vertex to the path
  path.push_back(start);

  // If current vertex is the destination vertex, then
  //   we have found one path.
  //   Add this cell to the Tran_Mat_Object that is tracking
  //   this transition matrix cell.
  if (start == end) {
    vertices_to_keep.insert(path.begin(), path.end());
  }
  else if (cutoff != 0) {
    cutoff--;

    // Recursively process all the vertices adjacent to the current vertex
    for_each(this->successors(start), [&](int v) {
      if (!(*this)[v].visited) {
        this->get_subgraph_between(v, end, path, vertices_to_keep, cutoff);
      }
    });
  }

  // Remove current vertex from the path and make it unvisited
  path.pop_back();
  (*this)[start].visited = false;
};

AnalysisGraph AnalysisGraph::get_subgraph_for_concept(string concept,
                                                      bool inward,
                                                      int depth) {

  using ranges::to;
  using namespace boost::adaptors;

  // Mark all the vertices as not visited
  for_each(this->nodes(), [](Node& node) { node.visited = false; });

  int num_verts = this->num_vertices();

  unordered_set<int> vertices_to_keep = unordered_set<int>();

  this->get_subgraph(
      this->get_vertex_id(concept), vertices_to_keep, depth, inward);

  unordered_set<string> nodes_to_remove =
      this->node_indices() |
      filtered([&](int v) { return !in(vertices_to_keep, v); }) |
      transformed([&](int v) { return (*this)[v].name; }) | to<unordered_set>();

  if (vertices_to_keep.size() == 0) {
    print("Subgraph has 0 nodes - returning an empty CAG!");
  }

  // Make a copy of current AnalysisGraph
  // TODO: We have to make sure that we are making a deep copy.
  //       Test so far does not show suspicious behavior
  AnalysisGraph G_sub = *this;
  for_each(nodes_to_remove, [&](string n) { G_sub.remove_node(n); });
  G_sub.clear_state();
  return G_sub;
}

AnalysisGraph AnalysisGraph::get_subgraph_for_concept_pair(
    string source_concept, string target_concept, int cutoff) {
  int src_id = this->get_vertex_id(source_concept);
  int tgt_id = this->get_vertex_id(target_concept);

  unordered_set<int> vertices_to_keep;
  unordered_set<string> vertices_to_remove;
  vector<int> path;

  // Mark all the vertices are not visited
  for_each(this->node_indices(), [&](int v) { (*this)[v].visited = false; });

  this->get_subgraph_between(src_id, tgt_id, path, vertices_to_keep, cutoff);

  if (vertices_to_keep.size() == 0) {
    print("AnalysisGraph::get_subgraph_for_concept_pair(): "
         "There are no paths of length <= {0} from "
         "source concept {1} --to-> target concept {2}. "
         "Returning an empty CAG!",
         cutoff,
         source_concept,
         target_concept);
  }

  // Determine the vertices to be removed
  for (int vert_id : this->node_indices()) {
    if (!in(vertices_to_keep, vert_id)) {
      vertices_to_remove.insert((*this)[vert_id].name);
    }
  }

  // Make a copy of current AnalysisGraph
  // TODO: We have to make sure that we are making a deep copy.
  //       Test so far does not show suspicious behavior
  AnalysisGraph G_sub = *this;
  G_sub.remove_nodes(vertices_to_remove);
  G_sub.clear_state();

  return G_sub;
}