Commit 34a58031 authored by jonasseidel's avatar jonasseidel
Browse files

Graphtheory overhaul in anticipation of new graph generation system:

- Adding SubGraph class for easy manipulation of restricted graphs
parent 81e3a7cc
......@@ -3,33 +3,37 @@
#include <cstddef>
#include <random>
#include <set>
#include <unordered_set>
enum generation_type{
random_unused,
random_all_static,
random_all_dynamic
};
template <typename T>
class random_set_element_generator{
std::default_random_engine _engine;
std::set<T>* _set;
static std::default_random_engine _engine;
std::unordered_set<T>* _dynamic_set;
std::unordered_set<T> _static_set;
std::unordered_set<T> _current_run_set;
generation_type _gen_type;
public:
random_set_element_generator(std::set<T>* s);
random_set_element_generator(std::unordered_set<T>* s, generation_type gen_type);
random_set_element_generator(std::unordered_set<T> s, generation_type gen_type);
void reset();
bool has_next();
T next();
void operator>>(T& var);
};
template <typename T>
class random_set_element_generator_capture{
std::default_random_engine _engine;
std::set<T> _set;
bool _random_unused;
std::default_random_engine random_set_element_generator<T>::_engine((std::random_device())());
public:
random_set_element_generator_capture(std::set<T>& s, bool force_unused = false);
T next();
void operator>>(T& var);
};
#include "random_set_element_generator.ipp"
......
template <typename T>
random_set_element_generator<T>::random_set_element_generator(std::set<T>* s) : _engine((std::random_device())()), _set(s){}
random_set_element_generator<T>::random_set_element_generator(std::unordered_set<T>* s, generation_type gen_type) : _gen_type(gen_type){
switch(this->_gen_type){
case random_unused:
this->_current_run_set = this->_static_set = *s;
break;
case random_all_static:
this->_static_set = *s;
break;
case random_all_dynamic:
this->_dynamic_set = s;
break;
default:
throw std::invalid_argument("unknown generation_type in contructor of random_set_element_generator");
}
}
template <typename T>
random_set_element_generator<T>::random_set_element_generator(std::unordered_set<T> s, generation_type gen_type) : _gen_type(gen_type){
switch(this->_gen_type){
case random_unused:
this->_current_run_set = this->_static_set = std::move(s);
break;
case random_all_static:
this->_static_set = std::move(s);
break;
case random_all_dynamic:
throw std::invalid_argument("reference needed to generate dynamically");
default:
throw std::invalid_argument("unknown generation_type in contructor of random_set_element_generator");
}
}
template <typename T>
T random_set_element_generator<T>::next(){
std::uniform_int_distribution<int> _dist(0, _set->size() - 1);
typename std::unordered_set<T>::iterator set_iterator;
size_t current_set_size;
switch(this->_gen_type){
case random_unused:
set_iterator = this->_current_run_set.begin();
current_set_size = this->_current_run_set.size();
break;
case random_all_static:
set_iterator = this->_static_set.begin();
current_set_size = this->_static_set.size();
break;
case random_all_dynamic:
set_iterator = this->_dynamic_set->begin();
current_set_size = this->_dynamic_set->size();
break;
default:
throw std::invalid_argument("unknown generation_type in contructor of random_set_element_generator");
}
if(current_set_size == 0) throw std::range_error("can't generate from emtpy set");
std::uniform_int_distribution<int> _dist(0, current_set_size - 1);
typename std::set<T>::iterator iter = this->_set->begin();
if(iter == this->_set->end()) throw std::range_error("can't generate from emtpy set");
size_t element_index = _dist(random_set_element_generator<T>::_engine);
std::advance(set_iterator, element_index);
T return_before_iter_invalid = *set_iterator;
size_t limit = _dist(_engine);
for(size_t i = 1; i <= limit; i++){
iter++;
if(this->_gen_type == random_unused){
this->_current_run_set.erase(set_iterator);
}
return *iter;
return return_before_iter_invalid;
}
template <typename T>
......@@ -21,31 +71,26 @@ void random_set_element_generator<T>::operator>>(T& var){
var = this->next();
}
template <typename T>
random_set_element_generator_capture<T>::random_set_element_generator_capture(std::set<T>& s, bool force_unused) : _engine((std::random_device())()), _set(s), _random_unused(force_unused){}
template <typename T>
T random_set_element_generator_capture<T>::next(){
std::uniform_int_distribution<int> _dist(0, _set.size() - 1);
typename std::set<T>::iterator iter = this->_set.begin();
if(iter == this->_set.end()) throw std::range_error("can't generate from emtpy set");
size_t limit = _dist(_engine);
for(size_t i = 1; i <= limit; i++){
iter++;
}
T ret = *iter;
if(this->_random_unused){
this->_set.erase(iter);
}
return ret;
void random_set_element_generator<T>::reset(){
this->_current_run_set = this->_set;
}
template <typename T>
void random_set_element_generator_capture<T>::operator>>(T& var){
var = this->next();
bool random_set_element_generator<T>::has_next(){
size_t current_set_size;
switch(this->_gen_type){
case random_unused:
current_set_size = this->_current_run_set.size();
break;
case random_all_static:
current_set_size = this->_static_set.size();
break;
case random_all_dynamic:
current_set_size = this->_dynamic_set->size();
break;
default:
throw std::invalid_argument("unknown generation_type in contructor of random_set_element_generator");
}
return (current_set_size > 0);
}
......@@ -52,6 +52,17 @@ void Attribute::operator=(Attribute&& attr){
this->_value = std::move(attr._value);
}
bool Attribute::operator!=(const Attribute& other) const {
if(this->_dir != other._dir) return true;
if(this->_integrality != other._integrality) return true;
if(this->_value != other._value) return true;
return false;
}
bool Attribute::operator==(const Attribute& other) const {
return !(*this != other);
}
const opt_dir& Attribute::optimization_direction() const {
return this->_dir;
}
......
......@@ -11,7 +11,7 @@ enum opt_dir : int {max = 1, fix = 0, min = -1};
class Attribute{
opt_dir _dir;
bool _dependent;
bool _dependent; // misses proper support (never actually get set in constructor)
integrality _integrality;
double _value;
public:
......@@ -24,6 +24,9 @@ public:
void operator=(const Attribute& attr);
void operator=(Attribute&& attr);
bool operator!=(const Attribute& other) const ;
bool operator==(const Attribute& other) const ;
const opt_dir& optimization_direction() const;
opt_dir& optimization_direction();
const bool& is_dependent() const;
......
......@@ -2,17 +2,17 @@
#include "../Common/io_format.h"
Edge::Edge(const Edge& remote_edge, const std::map<const 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) {
Edge::Edge(const Edge& remote_edge, const std::unordered_map<const 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)){
Edge::Edge(Node* from, Node* to, std::string description, std::unordered_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){
Edge::Edge(std::istream& is, const std::unordered_map<std::string, Node* >& name_lookup){
std::string curr;
is >> curr;
IO_THROW(curr, "Edge");
......
#ifndef EDGE_H
#define EDGE_H
#include <set>
#include <map>
#include <cstddef>
#include <iostream>
#include <stdexcept>
#include <unordered_map>
#include <string>
#include <sstream>
#include <iostream>
#include <cassert>
#include <stdexcept>
#include "Attribute.h"
#include "Node.h"
......@@ -20,14 +19,14 @@ class Edge{
Node* _from;
Node* _to;
std::map<std::string, Attribute> _attributes;
std::unordered_map<std::string, Attribute> _attributes;
public:
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<const Node*, Node*>& node_lookup);
Edge(const Edge& remote_edge, const std::unordered_map<const Node*, Node*>& node_lookup);
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(Node* from, Node* to, std::string description, std::unordered_map<std::string, Attribute> attributes = {});
Edge(std::istream& is, const std::unordered_map<std::string, Node* >& name_lookup);
const std::string& description() const {
return this->_description;
......@@ -60,11 +59,11 @@ public:
return node == this->from() ? this->to() : this->from();
}
const std::map<std::string, Attribute>& attributes() const {
const std::unordered_map<std::string, Attribute>& attributes() const {
return this->_attributes;
}
std::map<std::string, Attribute>& attributes() {
std::unordered_map<std::string, Attribute>& attributes() {
return this->_attributes;
}
......
#include "random_attribute_generator.h"
random_attribute_generator::random_attribute_generator(
std::map<
std::unordered_map<
std::string,
std::tuple<opt_dir, integrality, double, double>
> data
......@@ -19,8 +19,8 @@ random_attribute_generator::random_attribute_generator(
}
}
std::map<std::string, Attribute> random_attribute_generator::next(){
std::map<std::string, Attribute> instance;
std::unordered_map<std::string, Attribute> random_attribute_generator::next(){
std::unordered_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}} );
......@@ -31,6 +31,6 @@ std::map<std::string, Attribute> random_attribute_generator::next(){
return instance;
}
void random_attribute_generator::operator>>(std::map<std::string, Attribute>& var){
void random_attribute_generator::operator>>(std::unordered_map<std::string, Attribute>& var){
var = this->next();
}
......@@ -2,12 +2,12 @@
#define RANDOM_ATTRIBUTE_GENERATOR_H
#include <cstddef>
#include <map>
#include <tuple>
#include <string>
#include <random>
#include <stdexcept>
#include <unordered_map>
#include <cassert>
#include <string>
#include <stdexcept>
#include "../Attribute.h"
......@@ -15,15 +15,15 @@
class random_attribute_generator{
std::default_random_engine _engine;
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;
std::unordered_map<std::string, std::pair<opt_dir, integrality>> _constr_data;
std::unordered_map<std::string, std::uniform_int_distribution<>> _integral_dist;
std::unordered_map<std::string, std::uniform_real_distribution<>> _continuous_dist;
public:
random_attribute_generator(std::map<std::string, std::tuple<opt_dir, integrality, double, double> > data);
random_attribute_generator(std::unordered_map<std::string, std::tuple<opt_dir, integrality, double, double> > data);
std::map<std::string, Attribute> next();
void operator>>(std::map<std::string, Attribute>& var);
std::unordered_map<std::string, Attribute> next();
void operator>>(std::unordered_map<std::string, Attribute>& var);
};
#endif
......@@ -4,8 +4,8 @@ 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
std::unordered_map<std::string, Attribute> default_values_edge_attributes,
std::unordered_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) {}
......@@ -25,12 +25,12 @@ Graph random_graph_generator::next(){
return g;
}
std::tuple<std::pair<Node*, Node*>, std::set<Edge*>, Graph> random_graph_generator::next_acyclic_2_tips(){
std::tuple<std::pair<Node*, Node*>, std::unordered_set<Edge*>, 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;
}
std::set<Edge*> core_network = g.edges();
std::unordered_set<Edge*> core_network = g.export_edges();
auto [source, target] = g.tip_fringes(this->_edge_generator, this->_node_generator);
......@@ -40,18 +40,25 @@ std::tuple<std::pair<Node*, Node*>, std::set<Edge*>, Graph> random_graph_generat
return {std::pair{source, target}, std::move(core_network), std::move(g)};
}
std::tuple<std::pair<Node*, Node*>, std::set<Edge*>, Graph> random_graph_generator::next_acyclic_in_steps_2_tips(size_t steps, size_t fuzzing, bool only_tip_fringes, bool only_tip_extreme_layer, bool shelter_orphans, random_attribute_generator* connector_generator, random_attribute_generator* terminal_generator){
std::tuple<std::pair<Node*, Node*>, std::unordered_set<Edge*>, Graph> random_graph_generator::next_acyclic_in_steps_2_tips(size_t steps, size_t fuzzing, bool only_tip_fringes, bool only_tip_extreme_layer, bool shelter_orphans, random_attribute_generator* connector_generator, random_attribute_generator* terminal_generator){
Graph g (this->_template_edge_attributes, this->_template_node_attributes);
std::tuple<bool, std::vector<std::set<Node*>>, std::pair<std::vector<Node*>, std::vector<Edge*>>> tpl;
while( !std::get<0>(tpl = std::move(random_graph_generator::grow_random_acyclic_in_steps(g, this->_number_of_nodes/steps, this->_number_of_edges/steps, steps, fuzzing, this->_edge_generator, this->_node_generator))) ){
std::cout << "generation failed; retrying!" << std::endl;
}
auto graph_generation_data = random_graph_generator::grow_random_acyclic_in_steps(
g,
this->_number_of_nodes/steps,
this->_number_of_edges/steps,
steps,
0, // fuzzing start
fuzzing,
this->_edge_generator,
this->_node_generator
);
std::set<Edge*> core_network = g.edges();
std::unordered_set<Edge*> core_network = g.export_edges();
if(connector_generator == nullptr) connector_generator = &this->_edge_generator;
if(terminal_generator == nullptr) terminal_generator = &this->_node_generator;
Node* source;
Node* target;
if(only_tip_fringes){
......@@ -60,7 +67,7 @@ std::tuple<std::pair<Node*, Node*>, std::set<Edge*>, Graph> random_graph_generat
if(only_tip_extreme_layer){
// tips first and last layer
auto [source_, target_] = g.tip_fringes(std::get<1>(tpl).front(), std::get<1>(tpl).back(), *connector_generator, *terminal_generator, shelter_orphans);
auto [source_, target_] = g.tip_fringes(std::get<0>(graph_generation_data).front(), std::get<0>(graph_generation_data).back(), *connector_generator, *terminal_generator, shelter_orphans);
source = source_;
target = target_;
}else{
......@@ -75,15 +82,15 @@ std::tuple<std::pair<Node*, Node*>, std::set<Edge*>, Graph> random_graph_generat
assert(only_tip_extreme_layer);
// only those nodes of the first and last generated layers (tipping just everything doesn't make sense)
Node* s = g.add_node(std::to_string(g.size().first), terminal_generator->next());
for(Node* n : std::get<1>(tpl).front()){
Node* s = g.add_node(std::to_string(g.size().nodes), terminal_generator->next());
for(Node* n : std::get<0>(graph_generation_data).front()){
if(!shelter_orphans && n->incident().size() == 0) continue;
std::stringstream name;
name << s->description() << "_" << n->description();
g.add_edge(s, n, name.str(), connector_generator->next());
}
Node* t = g.add_node(std::to_string(g.size().first), terminal_generator->next());
for(Node* n : std::get<1>(tpl).back()){
Node* t = g.add_node(std::to_string(g.size().nodes), terminal_generator->next());
for(Node* n : std::get<0>(graph_generation_data).back()){
if(!shelter_orphans && n->incident().size() == 0) continue;
std::stringstream name;
name << n->description() << "_" << t->description();
......@@ -146,25 +153,107 @@ random_attribute_generator& random_graph_generator::node_generator(){
}
std::pair<bool, std::pair<std::vector<Node*>, std::vector<Edge*>>> random_graph_generator::grow_random(Graph& g, size_t number_of_nodes, size_t number_of_edges, random_attribute_generator& edge_attribute_generator, random_attribute_generator& node_attribute_generator){
std::vector<Node*> added_nodes;
std::vector<Edge*> added_edges;
std::unordered_set<Edge*> random_graph_generator::connect_random(
SubGraph& graph_superunion,
SubGraph& subgraph_from,
SubGraph& subgraph_to,
random_attribute_generator& edge_attribute_generator,
bool at_least_strongly_connected,
bool at_least_weakly_connected,
bool acyclic,
bool simple,
bool anti_reflexive,
size_t at_least_number_of_edges
) {
if(at_least_strongly_connected && acyclic) throw std::logic_error("can't generate strongly connected acyclic graph");
std::unordered_set<Edge*> added_edges;
added_edges.reserve(at_least_number_of_edges);
random_set_element_generator<Node*> rand_stream_from(subgraph_from.export_nodes(), random_all_static);
random_set_element_generator<Node*> rand_stream_to(subgraph_to.export_nodes(), random_all_static);
/**
We opt for brute force because resticting the generation to only edges that conform to generation parameters while also keeping the distribution uniform is connected with considerable overhead.
DO NOT ATTEMPT TO GENERATE (close to) FULL GRAPHS OF ANY SIGNIFICANT SIZE WITH THIS!!!
*/
Node* from;
Node* to;
bool warned = false;
while(
(added_edges.size() < at_least_number_of_edges)
|| (at_least_strongly_connected && !graph_superunion.is_strongly_connected())
|| (at_least_weakly_connected && !graph_superunion.is_weakly_connected())
) {
size_t attempt = 0;
redo:
{
size_t problem_size_coeff = subgraph_from.size().nodes + subgraph_to.size().nodes;
if(attempt > problem_size_coeff && !warned){
warned = true;
std::cerr << "bad performance when generating graph" << std::endl;
}
if(attempt > problem_size_coeff*problem_size_coeff*problem_size_coeff){
std::cerr << "giving up on graph generation" << std::endl;
goto failed;
};
}
try{
rand_stream_from >> from;
rand_stream_to >> to;
}catch (std::range_error& e){
goto failed;
}
if(
( (simple && ((from == to) || (graph_superunion.directed_admissible_st_path(from, to).number_of_edges()) == 1)) )
|| (anti_reflexive && (from == to))
|| (acyclic && graph_superunion.directed_admissible_st_path(to, from).exists())
) {
attempt++;
goto redo;
}
std::stringstream name;
name << from->description() << "_" << to->description();
added_edges.insert(graph_superunion.add_edge(from, to, name.str(), edge_attribute_generator.next()));
}
return added_edges;
failed:
/*
cleanup: restore state; due to failure: remove the already added components
*/
for(Edge* e : added_edges){
graph_superunion.remove_edge(e);
}
throw excessive_graph_generation_runtime();
assert(false);
return {};
}
std::pair<bool, std::pair<std::unordered_set<Node*>, std::unordered_set<Edge*>>> random_graph_generator::grow_random(Graph& g, size_t number_of_nodes, size_t number_of_edges, random_attribute_generator& edge_attribute_generator, random_attribute_generator& node_attribute_generator){
std::unordered_set<Node*> added_nodes;
std::unordered_set<Edge*> added_edges;
added_nodes.reserve(number_of_nodes);
added_edges.reserve(number_of_edges);
for(size_t i = 0; i < number_of_nodes; ++i){
std::stringstream name;
name << g.size().first;
added_nodes.push_back(g.add_node(name.str(), node_attribute_generator.next()));
name << g.size().nodes;
added_nodes.insert(g.add_node(name.str(), node_attribute_generator.next()));
}
random_set_element_generator<Node*> rand_stream (&g.nodes());
random_set_element_generator<Node*> rand_stream (&g.nodes(), random_all_dynamic);
Node* n1;
Node* n2;
for(size_t i = 0; i < number_of_edges; ++i){
size_t attempt = 0;
redo:
if(attempt > g.nodes().size()*g.nodes().size()*g.nodes().size()){
if(attempt > g.size().nodes*g.size().nodes*g.size().nodes){
goto failed;
};
......@@ -176,8 +265,9 @@ std::pair<bool, std::pair<std::vector<Node*>, std::vector<Edge*>>> random_graph_
}
{
auto existing_path = g.directed_admissible_st_path(n1, n2);
if(existing_path.first && existing_path.second.number_of_edges() <= 1) {
Path path = g.directed_admissible_st_path(n1, n2);
// unique paths?
if(path.exists() && path.number_of_edges() <= 1) {
attempt++;
goto redo;
}
......@@ -185,7 +275,7 @@ std::pair<bool, std::pair<std::vector<Node*>, std::vector<Edge*>>> random_graph_
std::stringstream name;
name << n1->description() << "_" << n2->description();
added_edges.push_back(g.add_edge(n1, n2, name.str(), edge_attribute_generator.next()));
added_edges.insert(g.add_edge(n1, n2, name.str(), edge_attribute_generator.next()));
}
return {true,{added_nodes, added_edges}};
......@@ -203,25 +293,26 @@ std::pair<bool, std::pair<std::vector<Node*>, std::vector<Edge*>>> random_graph_
}
//TODO: add generate_random_edge and random node to node, edge and use additional parameters to generate random attributes for edges, nodes
std::pair<bool, std::pair<std::vector<Node*>, std::vector<Edge*>>> random_graph_generator::grow_random_acyclic(Graph& g, size_t number_of_nodes, size_t number_of_edges, random_attribute_generator& edge_attribute_generator, random_attribute_generator& node_attribute_generator){
std::vector<Node*> added_nodes;
std::vector<Edge*> added_edges;
std::pair<bool, std::pair<std::unordered_set<Node*>, std::unordered_set<Edge*>>> random_graph_generator::grow_random_acyclic(Graph& g, size_t number_of_nodes, size_t number_of_edges, random_attribute_generator& edge_attribute_generator, random_attribute_generator& node_attribute_generator){
std::unordered_set<Node*> added_nodes;
std::unordered_set<Edge*> added_edges;