Program Listing for File graph_building.cpp
↰ Return to documentation for file (lib/graph_building.cpp
)
#include "AnalysisGraph.hpp"
using namespace std;
using namespace delphi::utils;
using fmt::print;
using namespace fmt::literals;
/*
============================================================================
Public: Graph Building
============================================================================
*/
int AnalysisGraph::add_node(string concept) {
if (!in(this->name_to_vertex, concept)) {
int v = boost::add_vertex(this->graph);
this->name_to_vertex[concept] = v;
(*this)[v].name = concept;
this->head_nodes.insert(v);
}
return this->name_to_vertex[concept];
}
bool AnalysisGraph::add_edge(CausalFragment causal_fragment) {
Event subject = Event(causal_fragment.first);
Event object = Event(causal_fragment.second);
string subj_name = subject.concept_name;
string obj_name = object.concept_name;
if (subj_name.compare(obj_name) != 0) { // Guard against self loops
// Add the nodes to the graph if they are not in it already
int subj_id = this->add_node(subj_name);
int obj_id = this->add_node(obj_name);
// Object is a dependent node
this->body_nodes.insert(obj_id);
// If Object had been an independent node, it no linger is
if (this->head_nodes.find(obj_id) != this->head_nodes.end()) {
this->head_nodes.erase(obj_id);
}
// If Subject was not a dependent node, it is independent
// if (this->body_nodes.find(subj_id) == this->body_nodes.end()) {
// this->head_nodes.insert(subj_id);
// }
auto [e, exists] = this->add_edge(subj_name, obj_name);
this->graph[e].evidence.push_back(Statement{subject, object});
return true;
}
else {
print("AnalysisGraph::add_edge\n"
"\tWARNING: Prevented adding a self loop for the concept {}\n",
subj_name);
return false;
}
}
void AnalysisGraph::add_edge(CausalFragmentCollection causal_fragments) {
EventCollection subjects = causal_fragments.first;
EventCollection objects = causal_fragments.second;
string subj_name = get<2>(subjects);
string obj_name = get<2>(objects);
int num_subj = get<0>(subjects).size();
int num_obj = get<0>(objects).size();
if (subj_name.compare(obj_name) != 0) { // Guard against self loops
// Add the nodes to the graph if they are not in it already
this->add_node(subj_name);
this->add_node(obj_name);
auto [e, exists] = this->add_edge(subj_name, obj_name);
string subject_adj;
string object_adj ;
int subject_pol;
int object_pol;
for(int stmt = 0; stmt < max(num_subj, num_obj); stmt++){
if(stmt < num_subj){
subject_adj = get<0>(subjects)[stmt] ;
subject_pol = get<1>(subjects)[stmt];
} else {
subject_adj = "None";
subject_pol = 1;
}
if(stmt < num_obj){
object_adj = get<0>(objects)[stmt] ;
object_pol = get<1>(objects)[stmt];
} else {
object_adj = "None";
object_pol = 1;
}
Event subject = Event(subject_adj, subject_pol, subj_name);
Event object = Event(object_adj, object_pol, obj_name);
this->graph[e].evidence.push_back(Statement{subject, object});
}
} else {
print("AnalysisGraph::add_edge\n"
"\tWARNING: Prevented adding a self loop for the concept {}\n",
subj_name);
}
}
pair<EdgeDescriptor, bool> AnalysisGraph::add_edge(int source, int target) {
// In Boost Graph Library, we are using vecS as the OutEdgeList,
// So, we have to check whether an edge exists from source to target before
// adding to prevent adding multiple edges between them in the same direction.
pair<EdgeDescriptor, bool> edge = boost::edge(source, target, this->graph);
if (!edge.second) {
edge = boost::add_edge(source, target, this->graph);
// Object is a dependent node
this->body_nodes.insert(target);
// If Object had been an independent node, it no linger is
if (this->head_nodes.find(target) != this->head_nodes.end()) {
this->head_nodes.erase(target);
}
}
return edge;
}
pair<EdgeDescriptor, bool> AnalysisGraph::add_edge(int source, string target) {
return this->add_edge(source, this->get_vertex_id(target));
}
pair<EdgeDescriptor, bool> AnalysisGraph::add_edge(string source, int target) {
return this->add_edge(this->get_vertex_id(source), target);
}
pair<EdgeDescriptor, bool> AnalysisGraph::add_edge(string source,
string target) {
return this->add_edge(this->get_vertex_id(source),
this->get_vertex_id(target));
}
/*
* The refactoring of the remove_node() method was buggy.
* It caused AnalysisGraph to crash.
* I replaced it with the previous implementation
*/
void AnalysisGraph::remove_node(string concept) {
auto node_to_remove = this->name_to_vertex.extract(concept);
if (node_to_remove) // Concept is in the CAG
{
// Note: This is an overloaded private method that takes in a vertex id
this->remove_node(node_to_remove.mapped());
}
else // The concept is not in the graph
{
throw out_of_range("Concept \"{}\" not in CAG!"_format(concept));
}
}
void AnalysisGraph::remove_nodes(unordered_set<string> concepts) {
vector<string> invalid_concepts;
for (string concept : concepts) {
auto node_to_remove = this->name_to_vertex.extract(concept);
// Concept is in the CAG
if (node_to_remove) {
// Note: This is an overlaoded private method that takes in a vertex id
this->remove_node(node_to_remove.mapped());
}
else // Concept is not in the CAG
{
invalid_concepts.push_back(concept);
}
}
if (invalid_concepts.size() > 0) {
// There were some invalid concepts
print("AnalysisGraph::remove_nodes()\n"
"\tThe following concepts were not present in the CAG!\n");
for (string invalid_concept : invalid_concepts) {
cerr << "\t\t" << invalid_concept << endl;
}
}
}
void AnalysisGraph::remove_edge(string src, string tgt) {
// Remove the edge
boost::remove_edge(
this->get_vertex_id(src), this->get_vertex_id(tgt), this->graph);
}
void AnalysisGraph::remove_edges(vector<pair<string, string>> edges) {
vector<pair<int, int>> edge_ids = vector<pair<int, int>>(edges.size());
set<string> invalid_sources;
set<string> invalid_targets;
set<pair<string, string>> invalid_edges;
std::transform(edges.begin(),
edges.end(),
edge_ids.begin(),
[this](pair<string, string> edge) {
int src_id;
int tgt_id;
// Flag invalid source vertices
try {
src_id = this->get_vertex_id(edge.first);
}
catch (const out_of_range& oor) {
src_id = -1;
}
// Flag invalid target vertices
try {
tgt_id = this->get_vertex_id(edge.second);
}
catch (const out_of_range& oor) {
tgt_id = -1;
}
// Flag invalid edges
if (src_id != -1 && tgt_id != -1) {
pair<int, int> edge_id = make_pair(src_id, tgt_id);
if (!in(this->beta2cell, edge_id)) {
src_id = -2;
}
}
return make_pair(src_id, tgt_id);
});
bool has_invalid_sources = false;
bool has_invalid_targets = false;
bool has_invalid_edges = false;
for (int e = 0; e < edge_ids.size(); e++) {
bool valid_edge = true;
if (edge_ids[e].first == -1) {
invalid_sources.insert(edges[e].first);
valid_edge = false;
has_invalid_sources = true;
}
if (edge_ids[e].second == -1) {
invalid_targets.insert(edges[e].second);
valid_edge = false;
has_invalid_targets = true;
}
if (edge_ids[e].first == -2) {
invalid_edges.insert(edges[e]);
valid_edge = false;
has_invalid_edges = true;
}
if (valid_edge) {
// Remove the edge
boost::remove_edge(edge_ids[e].first, edge_ids[e].second, this->graph);
}
}
if (has_invalid_sources || has_invalid_targets || has_invalid_edges) {
print("AnalysisGraph::remove_edges");
if (has_invalid_sources) {
cerr << "\tFollowing source vertexes are not in the CAG!" << endl;
for (string invalid_src : invalid_sources) {
cerr << "\t\t" << invalid_src << endl;
}
}
if (has_invalid_targets) {
cerr << "\tFollowing target vertexes are not in the CAG!" << endl;
for (string invalid_tgt : invalid_targets) {
cerr << "\t\t" << invalid_tgt << endl;
}
}
if (has_invalid_edges) {
cerr << "\tFollowing edges are not in the CAG!" << endl;
for (pair<string, string> invalid_edge : invalid_edges) {
cerr << "\t\t" << invalid_edge.first << " --to-> "
<< invalid_edge.second << endl;
}
}
}
}