Commit 0de0ab72 authored by Jonas Seidel's avatar Jonas Seidel

Major Graphtheory overhaul:

- Switching from enum indexed to String indexed Attributes 
- Merging unassociated function into classes 
- Supporting const variants of most Graphtheory classes 
- Increased Support for error handling 
- Correction to Graph copy assign operator 
- Renaming Basic_Graph.* to just Graph.* and Graph.h to Graphtheory.h 
- Eliminating Graph Specializations as they are no longer necessary 
- Adapting Maintenance_Problem classes to support new Attribute system
parent 4f35f342
#ifndef GRAPH_H
#define GRAPH_H
#include <set>
#include <map>
#include <vector>
#include <initializer_list>
#include <iostream>
#include <functional>
#include <queue>
#include <string>
#include "Edge.h"
#include "Node.h"
template <typename N, typename E>
class Graph{
std::set<Edge<N, E>*> _edges;
std::set<Node<N, E>*> _nodes;
std::map<N,Attribute> _template_node_attributes;
std::map<E,Attribute> _template_edge_attributes;
public:
// TODO: use references to speed up constructors
Graph(){};
Graph(std::map<E,Attribute> default_values_edge_attributes, std::map<N,Attribute> default_values_node_attributes = {});
Graph(Graph<N,E>& graph);
Graph(Graph<N,E>&& graph);
Edge<N,E>* add_edge(Node<N, E>* from, Node<N, E>* to, std::map<E,Attribute> edge_attributes = {}, std::string description = "");
Edge<N,E>* add_edge(Node<N, E>* from, Node<N, E>* to, std::map<E,double> edge_attributes = {}, std::string description = "");
Edge<N,E>* add_edge(Edge<N,E>* remote_edge, std::map<Node<N,E>*, Node<N,E>*>& node_lookup);
Node<N,E>* add_node(std::map<N,Attribute> node_attributes = {}, std::string description = "");
Node<N,E>* add_node(std::map<N,double> node_attributes = {}, std::string description = "");
Node<N,E>* add_node(Node<N,E>* remote_node);
void remove_node(Node<N, E>* node);
void remove_edge(Edge<N, E>* edge);
// change to non reference return type?
std::set<Edge<N, E>*>& edges(){
return this->_edges;
}
std::set<Node<N, E>*>& nodes(){
return this->_nodes;
}
Attribute node_template(E attr){
return this->_templatNode_attributes.find(attr)->second;
}
Attribute edge_template(E attr){
return this->_template_edge_attributes.find(attr)->second;
}
void reset_attribute_values(std::set<E> edge_attr, std::set<N> node_attr = {});
std::pair<size_t, size_t> size(){
return {this->_nodes.size(), this->_edges.size()};
}
~Graph(){
while(this->nodes().begin() != this->nodes().end()){
this->remove_node(*this->nodes().begin());
}
}
// _advanced.cpp:
Graph(std::istream& is);
void conditional_bfs_all_components(std::function<void(Node<N,E>* from, Edge<N,E>* via, bool used_in_traversal)> edge_exec, std::function<bool(Edge<N,E>* via, Node<N,E>* node)> node_exec, std::deque<Node<N,E>*> starting_nodes = {}, bool all_paths = false, std::function<bool(Node<N,E>* from, Edge<N,E>* via)> guide = [](Node<N,E>* n, Edge<N,E>* e)->bool {return e->from() == n;});
// _special_members_and_operators:
void operator=(const Graph<N,E>& graph);
void operator=(Graph<N,E>&& graph);
};
template <typename N, typename E>
std::ostream& operator<<(std::ostream& os, Graph<N,E>& g);
template <typename N, typename E>
std::istream& operator>>(std::istream& is, Graph<N,E>& g){g = Graph<N,E>(is); return is;}
#include "Basic_Graph.ipp"
#include "Basic_Graph_advanced.ipp"
#include "Basic_Graph_special_members_and_operators.ipp"
#endif
template <typename N, typename E>
Graph<N,E>::Graph(std::map<E,Attribute> default_values_edge_attributes, std::map<N,Attribute> default_values_node_attributes) : _template_node_attributes(default_values_node_attributes), _template_edge_attributes(default_values_edge_attributes){}
template <typename N, typename E>
Edge<N,E>* Graph<N,E>::add_edge(Node<N, E>* from, Node<N, E>* to, std::map<E,Attribute> edge_attributes, std::string description){
Edge<N, E>* ptr = new Edge<N, E>(from, to, edge_attributes, description);
this->edges().insert(ptr);
return ptr;
}
template <typename N, typename E>
Edge<N,E>* Graph<N, E>::add_edge(Node<N, E>* from, Node<N, E>* to, std::map<E,double> edge_attributes, std::string description){
Edge<N, E>* ptr = new Edge<N, E>(from, to, this->_template_edge_attributes, edge_attributes, description);
this->edges().insert(ptr);
return ptr;
}
template <typename N, typename E>
Edge<N,E>* Graph<N,E>::add_edge(Edge<N,E>* remote_edge, std::map<Node<N,E>*, Node<N,E>*>& node_lookup){
Edge<N, E>* ptr = new Edge<N, E>(*remote_edge, node_lookup);
this->edges().insert(ptr);
return ptr;
}
template <typename N, typename E>
Node<N,E>* Graph<N, E>::add_node(std::map<N,Attribute> node_attributes, std::string description){
Node<N, E>* ptr = new Node<N, E>(node_attributes, description);
this->nodes().insert(ptr);
return ptr;
}
template <typename N, typename E>
Node<N,E>* Graph<N, E>::add_node(std::map<N,double> node_attributes, std::string description){
Node<N, E>* ptr = new Node<N,E>(this->_template_node_attributes, node_attributes, description);
this->nodes().insert(ptr);
return ptr;
}
template <typename N, typename E>
Node<N,E>* Graph<N,E>::add_node(Node<N,E>* remote_node){
Node<N, E>* ptr = new Node<N,E>(*remote_node);
this->nodes().insert(ptr);
return ptr;
}
template <typename N, typename E>
void Graph<N, E>::remove_node(Node<N, E>* node){
for(Edge<N, E>* e : node->incident()){
this->edges().erase(e);
}
this->nodes().erase(node); // remove node pointer in graph
delete node; // removes pointers in node and edges & dealloc
}
template <typename N, typename E>
void Graph<N, E>::remove_edge(Edge<N, E>* edge){
this->edges().erase(edge);
delete edge;
}
template <typename N, typename E>
void Graph<N, E>::reset_attribute_values(std::set<E> edge_attr, std::set<N> node_attr){
for(Node<N, E>* n : this->nodes()){
for(const N attr : node_attr){
n->attribute(attr).value() = this->_template_node_attributes.find(attr)->second.value();
}
}
for(Edge<N, E>* e : this->edges()){
for(const E attr : edge_attr){
e->attribute(attr).value() = this->_template_edge_attributes.find(attr)->second.value();
}
}
}
template <typename N, typename E>
std::ostream& operator<<(std::ostream& os, Graph<N,E>& g){
if(&os == &std::cout){
os << "\033[0;31m";
os << "Graph { Nodes { \n";
for(Node<N, E>* n : g.nodes()){
os << *n << "\n";
}
os << "\033[0;31m";
os << "} Edges {" << "\n";
for(Edge<N, E>* e : g.edges()){
os << *e << "\n";
}
os << "\033[0;31m";
os << "} }";
os << "\033[0m";
}else{
os << "Graph { Nodes { \n";
for(Node<N, E>* n : g.nodes()){
os << *n << "\n";
}
os << "} Edges {" << "\n";
for(Edge<N, E>* e : g.edges()){
os << *e << "\n";
}
os << "} }";
}
return os;
}
template <typename N, typename E>
Edge<N,E>::Edge(std::istream& is, std::map<std::string, Node<N,E>* >& name_lookup){
#include "Edge.h"
Edge::Edge(const Edge& remote_edge, const std::map<Node*, Node*>& node_lookup) : _description(remote_edge._description), _from(node_lookup.find(remote_edge._from)->second), _to(node_lookup.find(remote_edge._to)->second), _attributes(remote_edge._attributes) {
this->_from->add_incident(this);
this->_to->add_incident(this);
}
Edge::Edge(Node* from, Node* to, std::string description, std::map<std::string, Attribute> attributes) : _description(std::move(description)), _from(from), _to(to), _attributes(std::move(attributes)){
from->add_incident(this);
to->add_incident(this);
};
Edge::Edge(std::istream& is, const std::map<std::string, Node* >& name_lookup){
std::string curr;
is >> curr;
assert(curr == "Edge");
......@@ -10,8 +21,7 @@ Edge<N,E>::Edge(std::istream& is, std::map<std::string, Node<N,E>* >& name_looku
assert(curr == "(");
is >> curr;
while(curr != ")"){
assert(curr.length() == 1);
E attribute = static_cast<E>(curr.c_str()[0]);
std::string attribute = curr;
is >> curr;
assert(curr == "=");
is >> curr;
......@@ -30,20 +40,29 @@ Edge<N,E>::Edge(std::istream& is, std::map<std::string, Node<N,E>* >& name_looku
assert(curr == "}");
}
template <typename N, typename E>
std::ostream& operator<<(std::ostream& os, Edge<N, E>& e){
void Edge::disconnect(){
this->from()->incident().erase(this);
this->to()->incident().erase(this);
}
void Edge::reconnect(){
this->from()->incident().insert(this);
this->to()->incident().insert(this);
}
std::ostream& operator<<(std::ostream& os, Edge& e){
if(&os == &std::cout){
os << "\033[0;32m";
os << "Edge " << e.description() << " ( ";
for(std::pair<E, Attribute> pair : e.attributes()){
os << static_cast<char>(pair.first) << " = " << pair.second.value() << ", ";
for(std::pair<std::string, Attribute> pair : e.attributes()){
os << pair.first << " = " << pair.second.value() << ", ";
}
os << ") {" << e.from()->description() << ", " << e.to()->description() << "}";
os << "\033[0m";
}else{
os << "Edge " << e.description() << " ( ";
for(std::pair<E, Attribute> pair : e.attributes()){
os << static_cast<char>(pair.first) << " = " << pair.second.value() << " ";
for(std::pair<std::string, Attribute> pair : e.attributes()){
os << pair.first << " = " << pair.second.value() << " ";
}
os << " ) { " << e.from()->description() << " " << e.to()->description() << " }";
}
......
......@@ -11,77 +11,94 @@
#include <cassert>
#include "Attribute.h"
#include "Node.h"
template <typename N, typename E> class Node;
class Node;
template <typename N, typename E>
class Edge{
std::string _description;
Node<N, E>* _from;
Node<N, E>* _to;
Node* _from;
Node* _to;
std::map<E, Attribute> _attributes;
std::map<std::string, Attribute> _attributes;
public:
Edge(Edge<N,E>& local_edge) : _description(local_edge._description), _from(local_edge._from), _to(local_edge._to), _attributes(local_edge._attributes) {}
Edge(Edge<N,E>&& local_edge) : _description(std::move(local_edge._description)), _from(std::move(local_edge._from)), _to(std::move(local_edge._to)), _attributes(std::move(local_edge._attributes)) {}
Edge(Edge<N,E>& remote_edge, std::map<Node<N,E>*, Node<N,E>*>& node_lookup) : _description(remote_edge._description), _from(node_lookup.find(remote_edge._from)->second), _to(node_lookup.find(remote_edge._to)->second), _attributes(remote_edge._attributes) {
this->_from->add_incident(this);
this->_to->add_incident(this);
}
Edge(Node<N, E>* from, Node<N, E>* to, std::string description = "") : _description(description), _from(from), _to(to){
from->add_incident(this);
to->add_incident(this);
};
Edge(Node<N, E>* from, Node<N, E>* to, std::map<E, Attribute>& default_attributes, std::map<E, double> attributes = {}, std::string description = "") : _description(description), _from(from), _to(to), _attributes(default_attributes){
for(std::pair<const E, double> pair : attributes){
this->attribute(pair.first).value() = pair.second;
}
Edge(const Edge& local_edge) : _description(local_edge._description), _from(local_edge._from), _to(local_edge._to), _attributes(local_edge._attributes) {}
Edge(Edge&& local_edge) : _description(std::move(local_edge._description)), _from(std::move(local_edge._from)), _to(std::move(local_edge._to)), _attributes(std::move(local_edge._attributes)) {}
Edge(const Edge& remote_edge, const std::map<Node*, Node*>& node_lookup);
from->add_incident(this);
to->add_incident(this);
};
Edge(Node* from, Node* to, std::string description, std::map<std::string, Attribute> attributes = {});
Edge(std::istream& is, const std::map<std::string, Node* >& name_lookup);
Edge(std::istream& is, std::map<std::string, Node<N,E>* >& name_lookup);
const std::string& description() const {
return this->_description;
}
std::string description(){
if(this->_description == ""){
std::stringstream name;
name << this;
return name.str();
}
std::string& description() {
return this->_description;
}
Node<N, E>* from(){
Node* const & from() const {
return this->_from;
}
Node*& from() {
return this->_from;
}
Node<N, E>* to(){
Node* const & to() const {
return this->_to;
}
Node*& to() {
return this->_to;
}
Node<N, E>* to(const Node<N, E>* node){
Node* const & to(const Node* node) const {
if(node != this->to() && node != this->from()) throw std::range_error("node not incident to edge");
return node == this->from() ? this->to() : this->from();
}
Node*& to(const Node* node) {
if(node != this->to() && node != this->from()) throw std::range_error("node not incident to edge");
return node == this->from() ? this->to() : this->from();
}
std::map<E, Attribute> attributes(){
const std::map<std::string, Attribute>& attributes() const {
return this->_attributes;
}
std::map<std::string, Attribute>& attributes() {
return this->_attributes;
}
std::pair<bool, Attribute> attribute(const std::string& attr) const {
auto search = this->_attributes.find(attr);
if(search == this->_attributes.end()){
return {false, {fix, 0}};
}
return {true, search->second};
}
Attribute& attribute(E key){
return this->_attributes.find(key)->second;
Attribute& attribute_throwing(const std::string& attr){
auto search = this->_attributes.find(attr);
if(search == this->_attributes.end()){
std::stringstream text;
text << "\"" << attr << "\" is not defined for Node " << this->description();
throw std::range_error(text.str());
}
return search->second;
}
void disconnect(){
this->from()->incident().erase(this);
this->to()->incident().erase(this);
void disconnect();
void reconnect();
void operator=(const Edge& edge) {
this->_description = edge._description;
this->_from = edge._from;
this->_to = edge._to;
}
void reconnect(){
this->from()->incident().insert(this);
this->to()->incident().insert(this);
void operator=(Edge&& edge){
this->_description = std::move(edge._description);
this->_from = std::move(edge._from);
this->_to = std::move(edge._to);
}
~Edge(){
......@@ -89,9 +106,6 @@ public:
}
};
template <typename N, typename E>
std::ostream& operator<<(std::ostream& os, Edge<N, E>& e);
#include "Edge.ipp"
std::ostream& operator<<(std::ostream& os, Edge& e);
#endif
#include "random_attribute_generator.h"
random_attribute_generator::random_attribute_generator(
std::map<
std::string,
std::tuple<opt_dir, integrality, double, double>
> data
){
for(std::pair<std::string, std::tuple<opt_dir, integrality, double, double> > pair : data){
assert(std::get<2>(pair.second) < std::get<3>(pair.second) );
std::pair<opt_dir, integrality> constr_data = {std::get<0>(pair.second), std::get<1>(pair.second)};
this->_constr_data.insert({pair.first, constr_data});
if(constr_data.second == Integral){
this->_integral_dist.insert({pair.first, std::uniform_int_distribution<>(std::get<2>(pair.second), std::get<3>(pair.second)) });
}else{
this->_continuous_dist.insert({pair.first, std::uniform_real_distribution<>(std::get<2>(pair.second), std::get<3>(pair.second)) });
}
}
}
std::map<std::string, Attribute> random_attribute_generator::next(){
std::map<std::string, Attribute> instance;
for(std::pair<std::string, std::pair<opt_dir, integrality> > constr_pair : this->_constr_data){
if(constr_pair.second.second == Integral){
instance.insert({constr_pair.first, {constr_pair.second.first, (double) (this->_integral_dist.find(constr_pair.first)->second)(this->_engine), constr_pair.second.second}} );
}else{
instance.insert({constr_pair.first, {constr_pair.second.first, (this->_continuous_dist.find(constr_pair.first)->second)(this->_engine), constr_pair.second.second}});
}
}
return instance;
}
void random_attribute_generator::operator>>(std::map<std::string, Attribute>& var){
var = this->next();
}
......@@ -7,25 +7,23 @@
#include <random>
#include <stdexcept>
#include <cassert>
#include <string>
#include "../Attribute.h"
template <typename T>
class random_attribute_generator{
std::default_random_engine _engine;
std::map<T, integrality> _integrality;
std::map<T, std::uniform_int_distribution<>> _integral_dist;
std::map<T, std::uniform_real_distribution<>> _continuous_dist;
std::map<std::string, std::pair<opt_dir, integrality>> _constr_data;
std::map<std::string, std::uniform_int_distribution<>> _integral_dist;
std::map<std::string, std::uniform_real_distribution<>> _continuous_dist;
public:
random_attribute_generator(std::map<T,std::tuple<integrality, double, double>> ranges);
random_attribute_generator(std::map<std::string, std::tuple<opt_dir, integrality, double, double> > data);
std::map<T, double> next();
void operator>>(std::map<T,double>& var);
std::map<std::string, Attribute> next();
void operator>>(std::map<std::string, Attribute>& var);
};
#include "random_attribute_generator.ipp"
#endif
template <typename T>
random_attribute_generator<T>::random_attribute_generator(std::map<T,std::tuple<integrality, double, double>> ranges){
for(std::pair<T, std::tuple<integrality, double, double>> pair : ranges){
assert(std::get<1>(pair.second) < std::get<2>(pair.second));
integrality integrality = std::get<0>(pair.second);
this->_integrality.insert({pair.first, integrality});
if(integrality == Integral){
this->_integral_dist.insert({pair.first, std::uniform_int_distribution<>(std::get<1>(pair.second), std::get<2>(pair.second))});
}else{
this->_continuous_dist.insert({pair.first, std::uniform_real_distribution<>(std::get<1>(pair.second), std::get<2>(pair.second))});
}
}
}
template <typename T>
std::map<T, double> random_attribute_generator<T>::next(){
std::map<T, double> instance;
for(std::pair<T, integrality> inty_pair : this->_integrality){
if(inty_pair.second == Integral){
instance.insert({inty_pair.first, (this->_integral_dist.find(inty_pair.first)->second)(this->_engine)});
}else{
instance.insert({inty_pair.first, (this->_continuous_dist.find(inty_pair.first)->second)(this->_engine)});
}
}
return instance;
}
template <typename T>
void random_attribute_generator<T>::operator>>(std::map<T,double>& var){
var = this->next();
}
template <typename N, typename E>
random_graph_generator<N,E>::random_graph_generator(std::map<E,Attribute> default_values_edge_attributes, std::map<N,Attribute> default_values_node_attributes, size_t number_of_edges, random_attribute_generator<E> edge_generator, size_t number_of_nodes, random_attribute_generator<N> node_generator)
: _default_values_node_attributes(default_values_node_attributes), _default_values_edge_attributes(default_values_edge_attributes), _number_of_nodes(number_of_nodes), _number_of_edges(number_of_edges), _node_generator(node_generator), _edge_generator(edge_generator) {}
template <typename N, typename E>
Graph<N,E> random_graph_generator<N,E>::next_acyclic(){
Graph<N,E> g (this->default_values_edge_attributes, this->default_values_edge_attributes);
while(!random_graph_generator::grow_random_acyclic(g, this->_number_of_nodes, this->_number_of_edges, this->_edge_generator, this->_node_generator)){
#include "random_graph_generator.h"
random_graph_generator::random_graph_generator(
size_t number_of_edges, size_t number_of_nodes,
random_attribute_generator edge_generator,
random_attribute_generator node_generator,
std::map<std::string, Attribute> default_values_edge_attributes,
std::map<std::string, Attribute> default_values_node_attributes
)
: _template_node_attributes(default_values_node_attributes), _template_edge_attributes(default_values_edge_attributes), _number_of_nodes(number_of_nodes), _number_of_edges(number_of_edges), _node_generator(node_generator), _edge_generator(edge_generator) {}
Graph random_graph_generator::next_acyclic(){
Graph g (this->_template_edge_attributes, this->_template_node_attributes);
while(!random_graph_generator::grow_random_acyclic(g, this->_number_of_nodes, this->_number_of_edges, this->_edge_generator, this->_node_generator).first){
std::cout << "generation failed; retrying!" << std::endl;
}
return g;
}
template <typename N, typename E>
Graph<N,E> random_graph_generator<N,E>::next(){
Graph<N,E> g (this->default_values_edge_attributes, this->default_values_edge_attributes);
while(!random_graph_generator::grow_random(g, this->_number_of_nodes, this->_number_of_edges, this->_edge_generator, this->_node_generator)){
Graph random_graph_generator::next(){
Graph g (this->_template_edge_attributes, this->_template_node_attributes);
while(!random_graph_generator::grow_random(g, this->_number_of_nodes, this->_number_of_edges, this->_edge_generator, this->_node_generator).first){
std::cout << "generation failed; retrying!" << std::endl;
}
return g;
}
template <typename N, typename E>
std::pair<std::pair<Node<N,E>*, Node<N,E>*>,Graph<N,E>> random_graph_generator<N,E>::next_acyclic_2_tips(){
Graph<N,E> g (this->_default_values_edge_attributes, this->_default_values_node_attributes);
std::pair<std::pair<Node*, Node*>,Graph> random_graph_generator::next_acyclic_2_tips(){
Graph g (this->_template_edge_attributes, this->_template_node_attributes);
while(!random_graph_generator::grow_random_acyclic(g, this->_number_of_nodes, this->_number_of_edges, this->_edge_generator, this->_node_generator).first){
std::cout << "generation failed; retrying!" << std::endl;
}
auto tips = tip_fringes(g, this->_edge_generator, this->_node_generator);
auto tips = g.tip_fringes(this->_edge_generator, this->_node_generator);
return {tips, std::move(g)};
}
template <typename N, typename E>
std::pair<bool, std::pair<std::vector<Node<N,E>*>, std::vector<Edge<N,E>*>>> random_graph_generator<N, E>::grow_random(Graph<N, E>& g, size_t number_of_nodes, size_t number_of_edges, random_attribute_generator<E>& edge_attribute_generator, random_attribute_generator<N>& node_attribute_generator){
std::vector<Node<N,E>*> added_nodes;
std::vector<Edge<N,E>*> added_edges;
std::pair<bool, Attribute> random_graph_generator::node_template(const std::string& attr) const{
auto search = this->_template_node_attributes.find(attr);
if(search == this->_template_node_attributes.end()){
return {false, {fix, 0}};
}
return {true, search->second};
}
std::pair<bool, Attribute> random_graph_generator::edge_template(const std::string& attr) const {