From a47d24ece76a0548ceb4a4730f6f480ddfb02e1f Mon Sep 17 00:00:00 2001 From: Jakob Gierschmann Date: Wed, 4 May 2022 01:24:46 +0200 Subject: [PATCH 01/25] Completed the porting of Dijkstra's Algorithm. Some small refactoring. --- CMakeLists.txt | 13 +++- STPFileParser.cpp | 4 +- ...ximator.cpp => SteinerTreeApproximator.cpp | 2 +- SteinerTreeApproximator.h | 19 ++++++ SteinerTreeAproximator.h | 18 ----- dijkstra/DijkstraGraph.cpp | 67 ++++++++++++++++--- dijkstra/DijkstraGraph.h | 6 +- dijkstra/FibonacciHeap.cpp | 16 +++-- dijkstra/FibonacciHeap.h | 4 +- graph/Edge.cpp | 5 -- graph/Edge.h | 34 ---------- graph/Graph.cpp | 21 +++--- graph/Graph.h | 7 +- main.cpp | 1 + 14 files changed, 119 insertions(+), 98 deletions(-) rename SteinerTreeAproximator.cpp => SteinerTreeApproximator.cpp (53%) create mode 100644 SteinerTreeApproximator.h delete mode 100644 SteinerTreeAproximator.h delete mode 100644 graph/Edge.cpp delete mode 100644 graph/Edge.h diff --git a/CMakeLists.txt b/CMakeLists.txt index c41d93a..c17df02 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,4 +3,15 @@ project(Hauptaufgabe_2) set(CMAKE_CXX_STANDARD 20) -add_executable(Hauptaufgabe_2 main.cpp SteinerTreeAproximator.cpp SteinerTreeAproximator.h STPFileParser.cpp STPFileParser.h dijkstra/DijkstraGraph.cpp dijkstra/DijkstraGraph.h graph/Graph.cpp graph/Graph.h graph/Edge.cpp graph/Edge.h dijkstra/FibonacciHeap.cpp dijkstra/FibonacciHeap.h) +add_executable( + Hauptaufgabe_2 + main.cpp + SteinerTreeApproximator.cpp + SteinerTreeApproximator.h + STPFileParser.cpp STPFileParser.h + dijkstra/DijkstraGraph.cpp + dijkstra/DijkstraGraph.h + graph/Graph.cpp graph/Graph.h + dijkstra/FibonacciHeap.cpp + dijkstra/FibonacciHeap.h +) diff --git a/STPFileParser.cpp b/STPFileParser.cpp index 50f8210..d3ed3df 100644 --- a/STPFileParser.cpp +++ b/STPFileParser.cpp @@ -61,7 +61,7 @@ Graph STPFileParser::create_graph() { bool STPFileParser::check_file_header(std::ifstream &file) { std::string line; std::getline(file, line); // read the first line of the file - // FIXME: wie kann ich hier eine Endlosschleife sicher vermeiden? + // TODO: wie kann ich hier eine Endlosschleife sicher vermeiden? while (line.empty() or !file.eof()) // find the first non-empty line { std::getline(file, line); @@ -91,7 +91,7 @@ void STPFileParser::read_graph_from_file(std::istream &file) { if (val_1 < 1 or val_1 > _num_nodes or val_2 < 1 or val_2 > _num_nodes) { throw std::invalid_argument("Invalid file foramt: Invalid edge."); } - // FIXME Hier sollte man auch gegen values die trotzdem 0 sind guarden + // TODO Hier sollte man auch gegen values die trotzdem 0 sind guarden // subtract 1 from node_id, this program is 0-based _edges.emplace_back(val_1 - 1, val_2 - 1, val_3); } diff --git a/SteinerTreeAproximator.cpp b/SteinerTreeApproximator.cpp similarity index 53% rename from SteinerTreeAproximator.cpp rename to SteinerTreeApproximator.cpp index ba79363..345baa1 100644 --- a/SteinerTreeAproximator.cpp +++ b/SteinerTreeApproximator.cpp @@ -2,4 +2,4 @@ // Created by jgier on 29.04.2022. // -#include "SteinerTreeAproximator.h" +#include "SteinerTreeApproximator.h" diff --git a/SteinerTreeApproximator.h b/SteinerTreeApproximator.h new file mode 100644 index 0000000..de44299 --- /dev/null +++ b/SteinerTreeApproximator.h @@ -0,0 +1,19 @@ +// +// Created by jgier on 29.04.2022. +// + +#ifndef HAUPTAUFGABE_2_STEINERTREEAPPROXIMATOR_H +#define HAUPTAUFGABE_2_STEINERTREEAPPROXIMATOR_H + + +#include + +class SteinerTreeApproximator { +public: + explicit SteinerTreeApproximator(std::string filename); + + int calculate(); +}; + + +#endif //HAUPTAUFGABE_2_STEINERTREEAPPROXIMATOR_H diff --git a/SteinerTreeAproximator.h b/SteinerTreeAproximator.h deleted file mode 100644 index 3bcdaac..0000000 --- a/SteinerTreeAproximator.h +++ /dev/null @@ -1,18 +0,0 @@ -// -// Created by jgier on 29.04.2022. -// - -#ifndef HAUPTAUFGABE_2_STEINERTREEAPROXIMATOR_H -#define HAUPTAUFGABE_2_STEINERTREEAPROXIMATOR_H - - -#include - -class SteinerTreeAproximator { -public: - explicit SteinerTreeAproximator(std::string filename); - int calculate(); -}; - - -#endif //HAUPTAUFGABE_2_STEINERTREEAPROXIMATOR_H diff --git a/dijkstra/DijkstraGraph.cpp b/dijkstra/DijkstraGraph.cpp index 2014e39..95ff5d7 100644 --- a/dijkstra/DijkstraGraph.cpp +++ b/dijkstra/DijkstraGraph.cpp @@ -11,14 +11,14 @@ DijkstraGraph::DijkstraGraph(Graph const &graph) { _nodes = std::vector(graph.num_nodes()); for (size_t i = 0; i < _nodes.size(); i++) { - _nodes[i]._id = i; + _nodes[i]._id = static_cast(i); } for (auto const &edge: graph.edges()) { - _nodes[edge._head].neighbours.push_back((edge._tail)); + _nodes[edge._head].neighbours.push_back(static_cast(edge._tail)); _nodes[edge._head].weights.push_back(edge._weight); - _nodes[edge._tail].neighbours.push_back((edge._head)); + _nodes[edge._tail].neighbours.push_back(static_cast(edge._head)); _nodes[edge._tail].weights.push_back(edge._weight); } } @@ -47,14 +47,63 @@ void DijkstraGraph::dijkstras_algorithm(NodeId root_node_id) { root_node.distance_to_root = 0; while (!candidates.empty()) { - NodeId node_id = candidates.extract_min(); - std::cout << "adding node " << node_id << std::endl; // DEBUG - Node &node = _nodes[node_id]; - node.included = true; + NodeId node_v_id = candidates.extract_min(); + //std::cout << "adding node_v " << node_v_id << std::endl; // DEBUG + Node &node_v = _nodes[node_v_id]; + node_v.included = true; + + // for (auto const& node_w : node_v.neighbours | std::views::transform(id_to_node_projection)) + // Fände ich auch schöner so, aber CLion beschwert sich da irgendwie. Vielleicht habe ich da was falsch + // konfiguriert. + for (auto const node_w_id: node_v.neighbours) { + Node &node_w = _nodes[node_w_id]; + WeightType edge_weight = node_v.weights[node_w_id]; + + if (node_w.distance_to_root == -1) { + + // TODO: extract method + node_w.predecessor = node_v_id; + node_w.distance_to_root = node_v.distance_to_root + edge_weight; + + candidates.insert(node_w_id, node_w.distance_to_root); + + // If the root node is the direct predecessor of w, that means that + // w must be a terminal, as the root was exclusively connected to the + // terminal nodes. + if (node_v_id == root_node_id) { + node_w.closest_terminal = node_w_id; + } + // Otherwise w has the same closest Terminal as v, if not updated later. + else { + node_w.closest_terminal = node_v.closest_terminal; + } + } + // Otherwise, if the old distance label is larger, we update it and decrease the + // key in the prioritiy queue + else if (node_w.distance_to_root > node_v.distance_to_root + edge_weight) { + + // TODO: extract method + node_w.distance_to_root = node_v.distance_to_root + edge_weight; + candidates.decrease_key(node_w_id, node_w.distance_to_root); + node_w.predecessor = node_v_id; + + // If the root node is the direct predecessor of w, that means that + // w must be a terminal, as the root was exclusively connected to the + // terminal nodes. + // Actually, the true case should never be reached, because the edge-weights + // from the root node are 0, but it is implemented for consistency. + if (node_v_id == root_node_id) { + node_w.closest_terminal = node_w_id; + } + // Otherwise w has the same closest Terminal as v, if not updated later. + else { + node_w.closest_terminal = node_v.closest_terminal; + } + + } - for (Node const &potential_candidate_node: node.neighbours | std::views::transform(id_to_node_projection)) { - std::cout << potential_candidate_node._id << std::endl; } + } diff --git a/dijkstra/DijkstraGraph.h b/dijkstra/DijkstraGraph.h index 187bf84..4cb8ad4 100644 --- a/dijkstra/DijkstraGraph.h +++ b/dijkstra/DijkstraGraph.h @@ -12,15 +12,15 @@ class DijkstraGraph { public: using NodeId = int; - using WeightVal = int; + using WeightType = int; struct Node { NodeId _id = -1; std::vector neighbours; - std::vector weights; + std::vector weights; NodeId predecessor = -1; NodeId closest_terminal = -1; - WeightVal distance_to_root = -1; + WeightType distance_to_root = -1; bool included = false; }; diff --git a/dijkstra/FibonacciHeap.cpp b/dijkstra/FibonacciHeap.cpp index aa6dcb8..9cf6866 100644 --- a/dijkstra/FibonacciHeap.cpp +++ b/dijkstra/FibonacciHeap.cpp @@ -5,8 +5,11 @@ #include #include #include +#include #include "FibonacciHeap.h" +// I am aware that this is largely c-style code. If I find the time I am going to refactor this. + FibonacciHeap::Node::Node(int item_id, int key) { parent = nullptr; is_binomial = true; @@ -36,7 +39,7 @@ void FibonacciHeap::insert(int item_id, int key) { } } -void FibonacciHeap::plant(Node *v) { +void FibonacciHeap::plant(Node *v) { // NOLINT(misc-no-recursion) v->is_binomial = true; if (_roots.size() < v->childs.size() + 1) { @@ -71,8 +74,13 @@ int FibonacciHeap::extract_min() { } for (size_t i = 0; i < _roots.size(); i++) { if (_roots[i] != nullptr) { - assert(_roots[i]->key >= 0 && _roots[i]->key <= 100); - assert(_roots[min_index]->key >= 0 && _roots[i]->key <= 100); + + // These asserts are only sensible, if I find a notion of knowing the maximum possible index, + // istead of choosing an arbitrary (large) value i.e. 10000. + + // assert(_roots[i]->key >= 0 && _roots[i]->key <= 10000); + // assert(_roots[min_index]->key >= 0 && _roots[i]->key <= 10000); + if (_roots[i]->key < _roots[min_index]->key) { min_index = i; } @@ -87,7 +95,7 @@ int FibonacciHeap::extract_min() { return min_node->item_id; } -int FibonacciHeap::find_min() const { +[[maybe_unused]] int FibonacciHeap::find_min() const { int min_index = 0; for (size_t i = 0; i < _roots.size(); i++) { if (_roots[i] != nullptr && _roots[i]->key < _roots[min_index]->key) { diff --git a/dijkstra/FibonacciHeap.h b/dijkstra/FibonacciHeap.h index 4bc744d..c7068b7 100644 --- a/dijkstra/FibonacciHeap.h +++ b/dijkstra/FibonacciHeap.h @@ -28,11 +28,11 @@ class FibonacciHeap { public: FibonacciHeap(); - void insert(int item_it, int key); + void insert(int item_id, int key); int extract_min(); - int find_min() const; + [[maybe_unused]] int find_min() const; void decrease_key(int item_id, int new_key); diff --git a/graph/Edge.cpp b/graph/Edge.cpp deleted file mode 100644 index 6638331..0000000 --- a/graph/Edge.cpp +++ /dev/null @@ -1,5 +0,0 @@ -// -// Created by jgier on 29.04.2022. -// - -#include "Edge.h" diff --git a/graph/Edge.h b/graph/Edge.h deleted file mode 100644 index 2d8f750..0000000 --- a/graph/Edge.h +++ /dev/null @@ -1,34 +0,0 @@ -// -// Created by jgier on 29.04.2022. -// - -#ifndef HAUPTAUFGABE_2_EDGE_H -#define HAUPTAUFGABE_2_EDGE_H - - -class Edge -{ -public: - //trivial constructor, nothing interesting - Edge(int node_a, int node_b, int weight = 1) - { - _nodes[0] = node_a; - _nodes[1] = node_b; - _weight = weight; - } - - // < is needed for sorting the edges. - bool operator<(Edge other) - { - if (_weight < other._weight) - return true; - return false; - } - - //Edges consist of their nodes and their weight. - int _nodes[2]; - int _weight; -}; - - -#endif //HAUPTAUFGABE_2_EDGE_H diff --git a/graph/Graph.cpp b/graph/Graph.cpp index f65f5a1..0359549 100644 --- a/graph/Graph.cpp +++ b/graph/Graph.cpp @@ -9,10 +9,8 @@ constexpr char FILE_HEADER[] = "33D32945 STP File, STP Format Version 1.0"; -Graph::Edge::Edge(Graph::NodeId node_a, Graph::NodeId node_b, int weight) : - _head(node_a), - _tail(node_b), - _weight(weight) {} +Graph::Edge::Edge(Graph::NodeId node_a, Graph::NodeId node_b, int weight) : _head(node_a), _tail(node_b), + _weight(weight) {} bool Graph::Edge::operator<(const Graph::Edge &other) const { if (_weight < other._weight) @@ -24,7 +22,7 @@ Graph::NodeId Graph::num_nodes() const { return _num_nodes; } -void Graph::print_graph(std::ostream outstream) { +void Graph::print_graph(std::basic_ostream &outstream) { outstream << FILE_HEADER << std::endl; outstream << std::endl; outstream << "SECTION Graph" << std::endl; @@ -43,11 +41,8 @@ const std::vector &Graph::edges() const { return _edges; } -Graph::Graph( - Graph::NodeId num_nodes, - std::vector terminals, - std::vector edges -) : - _num_nodes(num_nodes), - _terminals(std::move(terminals)), - _edges(std::move(edges)) {} +Graph::Graph(Graph::NodeId num_nodes, std::vector terminals, std::vector edges) : _num_nodes(num_nodes), + _terminals(std::move( + terminals)), + _edges(std::move( + edges)) {} diff --git a/graph/Graph.h b/graph/Graph.h index d22a23a..b3413f0 100644 --- a/graph/Graph.h +++ b/graph/Graph.h @@ -29,15 +29,10 @@ class Graph { [[nodiscard]] std::vector const &edges() const; - void print_graph(std::ostream outstream); + void print_graph(std::basic_ostream &outstream); private: - void read_graph_from_file(std::istream &file); - - void read_terminals_from_file(std::istream &file); - - NodeId _num_nodes; std::vector _terminals; std::vector _edges; diff --git a/main.cpp b/main.cpp index 1d585ac..1a33923 100644 --- a/main.cpp +++ b/main.cpp @@ -9,5 +9,6 @@ int main() { DijkstraGraph dijkstra_graph(graph); dijkstra_graph.dijkstras_algorithm(0); + graph.print_graph(std::cout); return 0; } From c0374e7458601f18f510a98945c436285bd0956f Mon Sep 17 00:00:00 2001 From: Jakob Gierschmann Date: Sun, 26 Jun 2022 13:47:03 +0200 Subject: [PATCH 02/25] swapped heap in Dijkstra's Algorithm --- dijkstra/DijkstraGraph.cpp | 3 ++- dijkstra/FibonacciHeap.cpp | 3 +-- dijkstra/FibonacciHeap.h | 12 ++++++------ dijkstra/StandardHeap.cpp | 35 +++++++++++++++++++++++++++++++++++ dijkstra/StandardHeap.h | 28 ++++++++++++++++++++++++++++ 5 files changed, 72 insertions(+), 9 deletions(-) create mode 100644 dijkstra/StandardHeap.cpp create mode 100644 dijkstra/StandardHeap.h diff --git a/dijkstra/DijkstraGraph.cpp b/dijkstra/DijkstraGraph.cpp index 95ff5d7..6a74f9d 100644 --- a/dijkstra/DijkstraGraph.cpp +++ b/dijkstra/DijkstraGraph.cpp @@ -41,7 +41,8 @@ void DijkstraGraph::dijkstras_algorithm(NodeId root_node_id) { node.included = false; } - FibonacciHeap candidates; + StandardHeap candidates; +// FibonacciHeap candidates; candidates.insert(root_node_id, 0); root_node.distance_to_root = 0; diff --git a/dijkstra/FibonacciHeap.cpp b/dijkstra/FibonacciHeap.cpp index 9cf6866..db6f962 100644 --- a/dijkstra/FibonacciHeap.cpp +++ b/dijkstra/FibonacciHeap.cpp @@ -5,7 +5,6 @@ #include #include #include -#include #include "FibonacciHeap.h" // I am aware that this is largely c-style code. If I find the time I am going to refactor this. @@ -95,7 +94,7 @@ int FibonacciHeap::extract_min() { return min_node->item_id; } -[[maybe_unused]] int FibonacciHeap::find_min() const { +int FibonacciHeap::find_min() const { int min_index = 0; for (size_t i = 0; i < _roots.size(); i++) { if (_roots[i] != nullptr && _roots[i]->key < _roots[min_index]->key) { diff --git a/dijkstra/FibonacciHeap.h b/dijkstra/FibonacciHeap.h index c7068b7..ad29f3d 100644 --- a/dijkstra/FibonacciHeap.h +++ b/dijkstra/FibonacciHeap.h @@ -9,7 +9,7 @@ #include #include -class FibonacciHeap { +class [[maybe_unused]] FibonacciHeap { private: struct Node { Node(int item_id, int key); @@ -28,15 +28,15 @@ class FibonacciHeap { public: FibonacciHeap(); - void insert(int item_id, int key); + [[maybe_unused]] void insert(int item_id, int key); - int extract_min(); + [[maybe_unused]] int extract_min(); - [[maybe_unused]] int find_min() const; + [[maybe_unused]] [[nodiscard]] int find_min() const; - void decrease_key(int item_id, int new_key); + [[maybe_unused]] void decrease_key(int item_id, int new_key); - bool empty() const; + [[maybe_unused]] [[nodiscard]] bool empty() const; ~FibonacciHeap(); diff --git a/dijkstra/StandardHeap.cpp b/dijkstra/StandardHeap.cpp new file mode 100644 index 0000000..ed9a7bd --- /dev/null +++ b/dijkstra/StandardHeap.cpp @@ -0,0 +1,35 @@ +// +// Created by jgier on 24.06.2022. +// + +#include "StandardHeap.h" + +void StandardHeap::insert(int item_id, int key) { + _heap.push_back({item_id, key}); + std::push_heap(_heap.begin(), _heap.end()); +} + +int StandardHeap::extract_min() { + int min_id = _heap.front().item_id; + std::pop_heap(_heap.begin(), _heap.end()); + _heap.pop_back(); + return min_id; +} + +void StandardHeap::decrease_key(int item_id, int new_key) { + for (int i = 0; i < _heap.size(); i++) { + if (_heap[i].item_id == item_id) { + _heap[i].key = new_key; + std::push_heap(_heap.begin(), _heap.begin() + i + 1); + break; + } + } +} + +bool StandardHeap::empty() const { + return _heap.empty(); +} + +bool StandardHeap::Node::operator<(const StandardHeap::Node &other) const { + return other.key < key; +} diff --git a/dijkstra/StandardHeap.h b/dijkstra/StandardHeap.h new file mode 100644 index 0000000..4eaf469 --- /dev/null +++ b/dijkstra/StandardHeap.h @@ -0,0 +1,28 @@ +// +// Created by jgier on 24.06.2022. +// + +#ifndef HAUPTAUFGABE_2_STANDARDHEAP_H +#define HAUPTAUFGABE_2_STANDARDHEAP_H + + +#include + +class StandardHeap { +private: + struct Node { + int item_id, key; + bool operator<(Node const &other) const; + }; + +public: + void insert(int item_id, int key); + int extract_min(); + void decrease_key(int item_id, int new_key); + [[nodiscard]] bool empty() const; +private: + std::vector _heap; +}; + + +#endif //HAUPTAUFGABE_2_STANDARDHEAP_H From a9b29a2634f04ebc61fc4f2a886dd4490cec6848 Mon Sep 17 00:00:00 2001 From: Jakob Gierschmann Date: Sun, 26 Jun 2022 14:26:38 +0200 Subject: [PATCH 03/25] first steiner calculations seem to be working fine --- CMakeLists.txt | 9 +++ DelaunayGraphFactory.cpp | 97 +++++++++++++++++++++++++++++++ DelaunayGraphFactory.h | 39 +++++++++++++ STPFileParser.cpp | 17 ++---- STPFileParser.h | 5 +- SteinerTreeApproximator.h | 2 +- dijkstra/DijkstraGraph.cpp | 52 ++++++++++++++--- dijkstra/DijkstraGraph.h | 18 ++++-- dijkstra/FibonacciHeap.cpp | 13 ++--- graph/Graph.cpp | 47 +++++++++++---- graph/Graph.h | 18 ++++-- graph/RectilinearGraph.cpp | 98 +++++++++++++++++++++++++++++++ graph/RectilinearGraph.h | 47 +++++++++++++++ kruskal/disjoint_set_2.h | 115 +++++++++++++++++++++++++++++++++++++ kruskal/kruskal_ind.cpp | 38 ++++++++++++ kruskal/kruskal_ind.h | 13 +++++ main.cpp | 22 +++++-- typedefs.h | 15 +++++ 18 files changed, 605 insertions(+), 60 deletions(-) create mode 100644 DelaunayGraphFactory.cpp create mode 100644 DelaunayGraphFactory.h create mode 100644 graph/RectilinearGraph.cpp create mode 100644 graph/RectilinearGraph.h create mode 100644 kruskal/disjoint_set_2.h create mode 100644 kruskal/kruskal_ind.cpp create mode 100644 kruskal/kruskal_ind.h create mode 100644 typedefs.h diff --git a/CMakeLists.txt b/CMakeLists.txt index c17df02..42c7fb5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,4 +14,13 @@ add_executable( graph/Graph.cpp graph/Graph.h dijkstra/FibonacciHeap.cpp dijkstra/FibonacciHeap.h + typedefs.h + DelaunayGraphFactory.cpp + DelaunayGraphFactory.h + kruskal/kruskal_ind.cpp + kruskal/kruskal_ind.h + dijkstra/StandardHeap.cpp + dijkstra/StandardHeap.h + graph/RectilinearGraph.cpp + graph/RectilinearGraph.h ) diff --git a/DelaunayGraphFactory.cpp b/DelaunayGraphFactory.cpp new file mode 100644 index 0000000..8a518f5 --- /dev/null +++ b/DelaunayGraphFactory.cpp @@ -0,0 +1,97 @@ +// +// Created by jgier on 20.06.2022. +// + +#include +#include +#include "DelaunayGraphFactory.h" + +void +DelaunayGraphFactory::bucket_sort( + std::vector &vector, + DelaunayGraphFactory::TypeToNodeId type_to_node_id, + NodeId max_id) { + std::vector> bucket_list; + bucket_list.resize(max_id); + for (auto const &t: vector) { + bucket_list[type_to_node_id(t)].emplace_back(t); + } + size_t orig_i = 0; + for (auto const &bucket: bucket_list) { + for (auto const &item: bucket) { + vector[orig_i] = item; + orig_i++; + } + } +} + +Graph DelaunayGraphFactory::create_delaunay_graph( + DijkstraGraph const &dijkstra_graph, + Graph const &graph +) { + assert(dijkstra_graph.calculation_finished()); + std::vector candidate_edges; + for (auto const &edge: graph.edges()) { + NodeId terminal_1 = dijkstra_graph[edge._head].closest_terminal; + NodeId terminal_2 = dijkstra_graph[edge._tail].closest_terminal; + // The distance to root equals the distance to the closest terminal + WeightType weight = dijkstra_graph[edge._head].distance_to_root + edge._weight + dijkstra_graph[edge._tail] + .distance_to_root; + if (terminal_1 != terminal_2) { + candidate_edges.emplace_back(terminal_1, terminal_2, weight); + } + } + + bucket_sort( + candidate_edges, + [](CandidateEdge const &candidate_edge) { return candidate_edge.smaller_terminal; }, + graph.num_nodes() + ); + + bucket_sort( + candidate_edges, + [](CandidateEdge const &candidate_edge) { return candidate_edge.larger_terminal; }, + graph.num_nodes() + ); + + std::vector edges; + + std::optional current_mincost_edge = std::nullopt; + + for (auto candidate_edge: candidate_edges) { + if (not current_mincost_edge.has_value()) { current_mincost_edge = candidate_edge; } + + else if (not( + current_mincost_edge->larger_terminal == candidate_edge.larger_terminal and + current_mincost_edge->smaller_terminal == candidate_edge.smaller_terminal + )) { + edges.emplace_back( + current_mincost_edge->smaller_terminal, + current_mincost_edge->larger_terminal, + current_mincost_edge->weight + ); + current_mincost_edge = candidate_edge; + } else if (current_mincost_edge->weight > + candidate_edge.weight) { current_mincost_edge = candidate_edge; } + } + edges.emplace_back( + current_mincost_edge->smaller_terminal, + current_mincost_edge->larger_terminal, + current_mincost_edge->weight + ); + + return {graph.num_nodes(), graph.terminals(), edges}; +} + +Graph DelaunayGraphFactory::create_delaunay_graph(const Graph &graph) { + DijkstraGraph dijkstra_graph(graph); + NodeId root_node = dijkstra_graph.add_node(graph.terminals(), std::vector(graph.terminals().size(), 0)); + dijkstra_graph.dijkstras_algorithm(root_node); + return create_delaunay_graph(dijkstra_graph, graph); +} + +DelaunayGraphFactory::CandidateEdge::CandidateEdge(NodeId terminal_1, NodeId terminal_2, WeightType weight) : + smaller_terminal(std::min(terminal_1, terminal_2)), + larger_terminal(std::max(terminal_1, terminal_2)), + weight(weight) {} + diff --git a/DelaunayGraphFactory.h b/DelaunayGraphFactory.h new file mode 100644 index 0000000..7b3cfb7 --- /dev/null +++ b/DelaunayGraphFactory.h @@ -0,0 +1,39 @@ +// +// Created by jgier on 20.06.2022. +// + +#ifndef HAUPTAUFGABE_2_DELAUNAYGRAPHFACTORY_H +#define HAUPTAUFGABE_2_DELAUNAYGRAPHFACTORY_H + + +#include "graph/Graph.h" +#include "dijkstra/DijkstraGraph.h" + +class DelaunayGraphFactory { +public: + + + static Graph create_delaunay_graph(Graph const &graph); + + static Graph create_delaunay_graph(DijkstraGraph const &dijkstra_graph, Graph const &graph); + +private: + struct CandidateEdge { + CandidateEdge(NodeId terminal_1, NodeId terminal_2, WeightType weight); + + // structuring the edges with smaller and larger terminals makes using them easier, as edges between the same + // nodes are grouped together in sorting. + NodeId smaller_terminal; + NodeId larger_terminal; + WeightType weight; + }; + + template + using TypeToNodeId = NodeId (*)(T const &element); + + static void + bucket_sort(std::vector &vector, TypeToNodeId type_to_node_id, NodeId max_id); +}; + + +#endif //HAUPTAUFGABE_2_DELAUNAYGRAPHFACTORY_H diff --git a/STPFileParser.cpp b/STPFileParser.cpp index d3ed3df..3375e2c 100644 --- a/STPFileParser.cpp +++ b/STPFileParser.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include "STPFileParser.h" constexpr char FILE_HEADER[] = "33D32945 STP File, STP Format Version 1.0"; @@ -55,19 +56,7 @@ STPFileParser::STPFileParser(std::string filename) : _filename(std::move(filenam } Graph STPFileParser::create_graph() { - return Graph(_num_nodes, _terminals, _edges); -} - -bool STPFileParser::check_file_header(std::ifstream &file) { - std::string line; - std::getline(file, line); // read the first line of the file - // TODO: wie kann ich hier eine Endlosschleife sicher vermeiden? - while (line.empty() or !file.eof()) // find the first non-empty line - { - std::getline(file, line); - } - // check for valid file header - return !(line == FILE_HEADER); + return {_num_nodes, _terminals, _edges}; } void STPFileParser::read_graph_from_file(std::istream &file) { @@ -137,3 +126,5 @@ std::ifstream STPFileParser::open_input_file() const { //check_file_header(file); return file; } + + diff --git a/STPFileParser.h b/STPFileParser.h index 1193c3a..b3053f8 100644 --- a/STPFileParser.h +++ b/STPFileParser.h @@ -16,7 +16,6 @@ class STPFileParser { Graph create_graph(); private: - static bool check_file_header(std::ifstream &file); [[nodiscard]] std::ifstream open_input_file() const; @@ -25,8 +24,8 @@ class STPFileParser { void read_terminals_from_file(std::istream &file); std::string _filename; - Graph::NodeId _num_nodes; - std::vector _terminals; + NodeId _num_nodes; + std::vector _terminals; std::vector _edges; }; diff --git a/SteinerTreeApproximator.h b/SteinerTreeApproximator.h index de44299..b14c410 100644 --- a/SteinerTreeApproximator.h +++ b/SteinerTreeApproximator.h @@ -8,7 +8,7 @@ #include -class SteinerTreeApproximator { +class [[maybe_unused]] SteinerTreeApproximator { public: explicit SteinerTreeApproximator(std::string filename); diff --git a/dijkstra/DijkstraGraph.cpp b/dijkstra/DijkstraGraph.cpp index 6a74f9d..016607b 100644 --- a/dijkstra/DijkstraGraph.cpp +++ b/dijkstra/DijkstraGraph.cpp @@ -2,13 +2,16 @@ // Created by jgier on 29.04.2022. // -#include #include +#include #include "DijkstraGraph.h" #include "FibonacciHeap.h" +#include "StandardHeap.h" +#include +#include -DijkstraGraph::DijkstraGraph(Graph const &graph) { +DijkstraGraph::DijkstraGraph(Graph const &graph) : _calculation_finished(false) { _nodes = std::vector(graph.num_nodes()); for (size_t i = 0; i < _nodes.size(); i++) { _nodes[i]._id = static_cast(i); @@ -23,10 +26,13 @@ DijkstraGraph::DijkstraGraph(Graph const &graph) { } } +const DijkstraGraph::Node &DijkstraGraph::operator[](NodeId index) const { + return _nodes[index]; +} void DijkstraGraph::dijkstras_algorithm(NodeId root_node_id) { - auto id_to_node_projection = [this](auto const node_id) { return _nodes[node_id]; }; + // auto id_to_node_projection = [this](auto const node_id) { return _nodes[node_id]; }; if (root_node_id >= _nodes.size()) { throw std::invalid_argument("Root node not in graph."); @@ -45,20 +51,22 @@ void DijkstraGraph::dijkstras_algorithm(NodeId root_node_id) { // FibonacciHeap candidates; candidates.insert(root_node_id, 0); + root_node.distance_to_root = 0; while (!candidates.empty()) { NodeId node_v_id = candidates.extract_min(); - //std::cout << "adding node_v " << node_v_id << std::endl; // DEBUG Node &node_v = _nodes[node_v_id]; node_v.included = true; // for (auto const& node_w : node_v.neighbours | std::views::transform(id_to_node_projection)) // Fände ich auch schöner so, aber CLion beschwert sich da irgendwie. Vielleicht habe ich da was falsch // konfiguriert. - for (auto const node_w_id: node_v.neighbours) { + for (size_t i = 0; i < node_v.neighbours.size(); i++) { + auto const node_w_id = node_v.neighbours[i]; + auto const edge_weight = node_v.weights[i]; + Node &node_w = _nodes[node_w_id]; - WeightType edge_weight = node_v.weights[node_w_id]; if (node_w.distance_to_root == -1) { @@ -100,12 +108,40 @@ void DijkstraGraph::dijkstras_algorithm(NodeId root_node_id) { else { node_w.closest_terminal = node_v.closest_terminal; } - } - } + } + _calculation_finished = true; +} +[[maybe_unused]] Graph DijkstraGraph::generate_output_graph() { + std::vector edges; + std::vector terminals; + + for (auto const &node: _nodes) { + if (node.predecessor != -1) { + WeightType edge_weight = node.distance_to_root - _nodes[node.predecessor].distance_to_root; + edges.emplace_back(static_cast(node._id), static_cast(node.predecessor), + edge_weight); + } } + return {static_cast(_nodes.size()), terminals, edges}; +} + +DijkstraGraph::Node &DijkstraGraph::operator[](NodeId index) { + return _nodes[index]; +} +NodeId DijkstraGraph::add_node(std::vector neighbours, std::vector weights) { + auto id = static_cast(_nodes.size()); + _nodes.push_back({id, std::move(neighbours), std::move(weights)}); + _calculation_finished = false; + return id; } + +bool DijkstraGraph::calculation_finished() const { return _calculation_finished; } + + + + diff --git a/dijkstra/DijkstraGraph.h b/dijkstra/DijkstraGraph.h index 4cb8ad4..dccf9de 100644 --- a/dijkstra/DijkstraGraph.h +++ b/dijkstra/DijkstraGraph.h @@ -7,12 +7,11 @@ #include #include +#include "../typedefs.h" #include "../graph/Graph.h" class DijkstraGraph { public: - using NodeId = int; - using WeightType = int; struct Node { NodeId _id = -1; @@ -24,16 +23,25 @@ class DijkstraGraph { bool included = false; }; + // TODO: make DijkstraGraph mutable + explicit DijkstraGraph(Graph const &graph); - Node &operator[](NodeId index) { - return _nodes[index]; - } + Node &operator[](NodeId index); + + Node const &operator[](NodeId index) const; void dijkstras_algorithm(NodeId root_node_id); + NodeId add_node(std::vector neighbours, std::vector weights); + + [[maybe_unused]] Graph generate_output_graph(); + + [[nodiscard]] bool calculation_finished() const; + private: std::vector _nodes; + bool _calculation_finished; }; diff --git a/dijkstra/FibonacciHeap.cpp b/dijkstra/FibonacciHeap.cpp index db6f962..303461e 100644 --- a/dijkstra/FibonacciHeap.cpp +++ b/dijkstra/FibonacciHeap.cpp @@ -7,8 +7,6 @@ #include #include "FibonacciHeap.h" -// I am aware that this is largely c-style code. If I find the time I am going to refactor this. - FibonacciHeap::Node::Node(int item_id, int key) { parent = nullptr; is_binomial = true; @@ -71,7 +69,7 @@ int FibonacciHeap::extract_min() { throw std::out_of_range("Heap is empty."); } } - for (size_t i = 0; i < _roots.size(); i++) { + for (int i = 0; i < _roots.size(); i++) { if (_roots[i] != nullptr) { // These asserts are only sensible, if I find a notion of knowing the maximum possible index, @@ -96,7 +94,7 @@ int FibonacciHeap::extract_min() { int FibonacciHeap::find_min() const { int min_index = 0; - for (size_t i = 0; i < _roots.size(); i++) { + for (int i = 0; i < _roots.size(); i++) { if (_roots[i] != nullptr && _roots[i]->key < _roots[min_index]->key) { min_index = i; } @@ -131,11 +129,10 @@ void FibonacciHeap::decrease_key(int item_id, int new_key) { } bool FibonacciHeap::empty() const { - for (Node *n: _roots) { - if (n != nullptr) - return false; + if (std::all_of(_roots.begin(), _roots.end(), [](Node *n) { return n == nullptr; })) { + return true; } - return true; + return false; } FibonacciHeap::~FibonacciHeap() { diff --git a/graph/Graph.cpp b/graph/Graph.cpp index 0359549..f6d46a1 100644 --- a/graph/Graph.cpp +++ b/graph/Graph.cpp @@ -9,8 +9,7 @@ constexpr char FILE_HEADER[] = "33D32945 STP File, STP Format Version 1.0"; -Graph::Edge::Edge(Graph::NodeId node_a, Graph::NodeId node_b, int weight) : _head(node_a), _tail(node_b), - _weight(weight) {} +Graph::Edge::Edge(NodeId head, NodeId tail, int weight) : _head(head), _tail(tail), _weight(weight) {} bool Graph::Edge::operator<(const Graph::Edge &other) const { if (_weight < other._weight) @@ -18,22 +17,31 @@ bool Graph::Edge::operator<(const Graph::Edge &other) const { return false; } -Graph::NodeId Graph::num_nodes() const { +NodeId Graph::num_nodes() const { return _num_nodes; } void Graph::print_graph(std::basic_ostream &outstream) { outstream << FILE_HEADER << std::endl; outstream << std::endl; - outstream << "SECTION Graph" << std::endl; + outstream << "SECTION Graph" << std::endl; outstream << "Nodes " << _num_nodes << std::endl; outstream << "Edges " << _edges.size() << std::endl; - for (const Edge &e: _edges) { - outstream << "E " << e._tail + 1 << " " << e._head + 1 << " " << e._weight << std::endl; + for (auto const &edge: _edges) { + outstream << "E " << edge._tail + 1 << " " << edge._head + 1 << " " << edge._weight << std::endl; } + outstream << "END" << std::endl; + outstream << std::endl; + outstream << "SECTION Terminals" << std::endl; + outstream << "Terminals " << _terminals.size() << std::endl; + for (auto const &terminal: _terminals) { + outstream << "T " << terminal + 1 << std::endl; + } outstream << "END" << std::endl; + outstream << std::endl; + outstream << "EOF" << std::endl; } @@ -41,8 +49,25 @@ const std::vector &Graph::edges() const { return _edges; } -Graph::Graph(Graph::NodeId num_nodes, std::vector terminals, std::vector edges) : _num_nodes(num_nodes), - _terminals(std::move( - terminals)), - _edges(std::move( - edges)) {} +Graph::Graph( + NodeId num_nodes, + std::vector terminals, + std::vector edges +) : + _num_nodes(num_nodes), + _terminals(std::move(terminals)), + _edges(std::move(edges)) {} + +const std::vector &Graph::terminals() const { + return _terminals; +} + +EdgeId Graph::num_edges() const { + return static_cast(_edges.size()); +} + +void Graph::add_edge(Graph::Edge const &new_edge) { + if (new_edge._head < _num_nodes and new_edge._tail < _num_nodes) { + _edges.push_back({new_edge}); + } +} diff --git a/graph/Graph.h b/graph/Graph.h index b3413f0..d4426a9 100644 --- a/graph/Graph.h +++ b/graph/Graph.h @@ -7,30 +7,38 @@ #include #include +#include "../typedefs.h" class Graph { public: - using EdgeId = size_t; - using NodeId = size_t; struct Edge { - Edge(NodeId node_a, NodeId node_b, int weight); + Edge(NodeId head, NodeId tail, int weight); - bool operator<(const Edge &other) const; + bool operator<(Edge const &other) const; NodeId _head; NodeId _tail; int _weight; }; + explicit Graph(NodeId num_nodes) : _num_nodes(num_nodes) {} + + Graph(NodeId num_nodes, std::vector terminals) : _num_nodes(num_nodes), _terminals(std::move(terminals)) {} + Graph(NodeId num_nodes, std::vector terminals, std::vector edges); - [[nodiscard]] NodeId num_nodes() const; + [[nodiscard]] NodeId virtual num_nodes() const; + [[nodiscard]] EdgeId num_edges() const; [[nodiscard]] std::vector const &edges() const; + [[nodiscard]] std::vector const &terminals() const; + void print_graph(std::basic_ostream &outstream); + void add_edge(Edge const &new_edge); + private: NodeId _num_nodes; diff --git a/graph/RectilinearGraph.cpp b/graph/RectilinearGraph.cpp new file mode 100644 index 0000000..d5870ff --- /dev/null +++ b/graph/RectilinearGraph.cpp @@ -0,0 +1,98 @@ +// +// Created by jgier on 26.06.2022. +// + +#include "RectilinearGraph.h" + +RectilinearGraph::RectilinearGraph(NodeId num_nodes) { + auto const sqrt = static_cast(std::sqrt(num_nodes)); + assert(sqrt * sqrt == num_nodes && "The number of nodes in a rect graph needs to be an integer sqare"); + + _grid_width = sqrt; + + _horizontal_edges_after_node = std::vector(num_nodes, false); + _vertical_edges_above_node = std::vector(num_nodes, false); +} + +RectilinearGraph::RectilinearGraph(NodeId num_nodes, std::vector terminals) : RectilinearGraph(num_nodes) { + _terminals = std::move(terminals); +} + +RectilinearGraph::RectilinearGraph(const Graph &graph) : RectilinearGraph(graph.num_nodes(), graph.terminals()) { + for (auto const &edge: graph.edges()) { + add_any_edge(edge._head, edge._tail); + } +} + +void RectilinearGraph::add_any_edge(NodeId node_a, NodeId node_b, bool flipped) { + assert(validate_node(node_a) && validate_node(node_b)); + swap_if_a_larger(node_a, node_b); + + if (flipped) { + while (node_b - node_a > node_b % _grid_width) { + _vertical_edges_above_node[node_a] = true; + node_a += _grid_width; + } + } + + while (node_a % _grid_width < node_b % _grid_width) { + _horizontal_edges_after_node[node_a] = true; + node_a += 1; + } + while (node_a % _grid_width > node_b % _grid_width) { + _horizontal_edges_after_node[node_b] = true; + node_b += 1; + } + + if (not flipped) { + while (node_b - node_a > node_b % _grid_width) { + _vertical_edges_above_node[node_a] = true; + node_a += _grid_width; + } + } +} + +std::optional RectilinearGraph::get_horizontal_connecting_edge(NodeId node_a, NodeId node_b) const { + swap_if_a_larger(node_a, node_b); + + if (node_b - node_a == 1 and (node_a + 1) % _grid_width == 0) { + return static_cast(node_a); + } else return std::nullopt; +} + +std::optional RectilinearGraph::get_vertical_conecting_edge(NodeId node_a, NodeId node_b) const { + swap_if_a_larger(node_a, node_b); + + if (node_a % _grid_width == node_b % _grid_width) { + return static_cast(node_a); + } else return std::nullopt; +} + + +void RectilinearGraph::swap_if_a_larger(NodeId &node_a, NodeId &node_b) { + NodeId temp = node_a; + node_a = std::min(node_a, node_b); + node_b = std::max(temp, node_b); +} + +NodeId RectilinearGraph::num_nodes() const { + return _grid_width * _grid_width; +} + +bool RectilinearGraph::validate_node(NodeId node) const { + return node >= 0 and node < num_nodes(); +} + +Graph RectilinearGraph::create_graph() const { + Graph result(num_nodes(), _terminals); + for (NodeId i = 0; i < num_nodes(); i++) { + if (_horizontal_edges_after_node[i]) { + result.add_edge({i, i + 1, 1}); + } + if (_vertical_edges_above_node[i]) { + result.add_edge({i, i + _grid_width, 1}); + } + } + return result; +} + diff --git a/graph/RectilinearGraph.h b/graph/RectilinearGraph.h new file mode 100644 index 0000000..a9466a0 --- /dev/null +++ b/graph/RectilinearGraph.h @@ -0,0 +1,47 @@ +// +// Created by jgier on 26.06.2022. +// + +#ifndef HAUPTAUFGABE_2_RECTILINEARGRAPH_H +#define HAUPTAUFGABE_2_RECTILINEARGRAPH_H + + +#include +#include +#include +#include "../typedefs.h" +#include "Graph.h" + +class RectilinearGraph { +public: + explicit RectilinearGraph(NodeId num_nodes); + + explicit RectilinearGraph(NodeId num_nodes, std::vector terminals); + + explicit RectilinearGraph(Graph const &graph); + + [[nodiscard]] NodeId num_nodes() const; + + void add_any_edge(NodeId node_a, NodeId node_b, bool flipped = true); + + [[nodiscard]] Graph create_graph() const; + +private: + NodeId _grid_width; + std::vector _terminals; + std::vector _horizontal_edges_after_node; + std::vector _vertical_edges_above_node; + + [[maybe_unused]] [[nodiscard]] std::optional + get_horizontal_connecting_edge(NodeId node_a, NodeId node_b) const; + + [[maybe_unused]] [[nodiscard]] std::optional + get_vertical_conecting_edge(NodeId node_a, NodeId node_b) const; + + [[nodiscard]] bool validate_node(NodeId node) const; + + static void swap_if_a_larger(NodeId &node_a, NodeId &node_b); +}; + + +#endif //HAUPTAUFGABE_2_RECTILINEARGRAPH_H diff --git a/kruskal/disjoint_set_2.h b/kruskal/disjoint_set_2.h new file mode 100644 index 0000000..34021f1 --- /dev/null +++ b/kruskal/disjoint_set_2.h @@ -0,0 +1,115 @@ +#pragma once + +#include +#include + +class Disjoint_Set { +public: + class Node { + public: + Node(); + + void set_parent(Node *parent); + + void increment_depth(); + + [[nodiscard]] Node *get_parent() const; + + [[nodiscard]] int get_depth() const; + + private: + Node *_parent; + int _depth; + }; + + bool set_equals(int a, int b); + + void make_sets(int num_new_sets); + + Node *find(Node *node); + + void unite(int a, int b); + + [[maybe_unused]] [[nodiscard]] int get_num_nodes() const; + +private: + std::vector _nodes; +}; + +//Hier waere dann eine inline section + +//z.B. +// inline bool Disjoint_Set::set_equals(int a, int b) { +// code +// } + +inline Disjoint_Set::Node::Node() { + _parent = this; + _depth = 0; +} + +// getter- and setter-methods +inline void Disjoint_Set::Node::set_parent(Node *parent) { + _parent = parent; +} + +inline void Disjoint_Set::Node::increment_depth() { + _depth++; +} + +inline Disjoint_Set::Node *Disjoint_Set::Node::get_parent() const { + return _parent; +} + +inline int Disjoint_Set::Node::get_depth() const { + return _depth; +} + +// end of class Node // + +// the functionality of -make_set- from the lecture has been expanded to +//create an arbitrary amount of sets at once. +inline void Disjoint_Set::make_sets(int num_new_sets) { + _nodes.resize(_nodes.size() + num_new_sets); +} + +// the -find- method from the lecture +inline Disjoint_Set::Node *Disjoint_Set::find(Node *node) { + Node *parent = node->get_parent(); + + if (parent != node) { + Node *new_parent = find(parent->get_parent()); + node->set_parent(new_parent); + } + + return node->get_parent(); +} + +// -union- method from the lecture (union is a keyword in c++, thus it's called unite) +inline void Disjoint_Set::unite(int a, int b) { + Node *a_ptr = &_nodes[a]; + Node *b_ptr = &_nodes[b]; + + Node *parent_a = find(a_ptr); + Node *parent_b = find(b_ptr); + + if (parent_a->get_depth() > parent_b->get_depth()) { + parent_b->set_parent(parent_a); + } else { + parent_a->set_parent(parent_b); + if (parent_a->get_depth() == parent_b->get_depth()) { + parent_b->increment_depth(); + } + } +} + +inline bool Disjoint_Set::set_equals(int a, int b) { + if (find(&_nodes[a]) == find(&_nodes[b])) + return true; + return false; +} + +inline int Disjoint_Set::get_num_nodes() const { + return static_cast(_nodes.size()); +} + diff --git a/kruskal/kruskal_ind.cpp b/kruskal/kruskal_ind.cpp new file mode 100644 index 0000000..fe1de79 --- /dev/null +++ b/kruskal/kruskal_ind.cpp @@ -0,0 +1,38 @@ +#include "kruskal_ind.h" +#include +#include +#include "disjoint_set_2.h" + +Graph kruskal(const Graph &input_graph) { + Graph return_graph(input_graph.num_nodes(), input_graph.terminals()); + + // vector erzeugen und mit den Zahlen [ 0, edges.size() ) fuellen + std::vector edge_ids(input_graph.num_edges()); + std::iota(edge_ids.begin(), edge_ids.end(), 0); + + auto id_to_edge_projection = [&input_graph](auto const edge_id) { return input_graph.edges()[edge_id]; }; + + std::sort(edge_ids.begin(), edge_ids.end(), + [&id_to_edge_projection](EdgeId a, EdgeId b) { + return (id_to_edge_projection(a) < id_to_edge_projection(b)); + }); + // iteriere ueber die edges in der reihenfolge, wie sie im id-vector gespeichert sind + // diese syntax range | adaptor, bedeutet, dass die range (hier edge_ids) erst durch die adaptor-funktion gejagt wird + // (transform ruft die uebergebene funktion einmal mit jedem element auf und ist dann eine range ueber die return-values der funktion) + // es gibt z.b. auch std::views::filter(unary_predicate), was dann nur die elemente durchlaesst, fuer die das unaere praedikat true zurueckgibt + + Disjoint_Set set; + set.make_sets(return_graph.num_nodes()); + for (auto edge_id: edge_ids) { + auto const &edge = id_to_edge_projection(edge_id); + + if (!set.set_equals(edge._head, edge._tail)) { + // unite the sets of the parents of the nodes of edge + set.unite(edge._head, edge._tail); + // add edge to the vector of included edges + return_graph.add_edge(edge); + } + } + + return return_graph; +} \ No newline at end of file diff --git a/kruskal/kruskal_ind.h b/kruskal/kruskal_ind.h new file mode 100644 index 0000000..f1c712c --- /dev/null +++ b/kruskal/kruskal_ind.h @@ -0,0 +1,13 @@ +#pragma once + +#include +#include "disjoint_set_2.h" +#include "../graph/Graph.h" + +//FIXED das Resultat returnen, nicht output parameter +//FIXED variablen ausschreiben +//FIXED definition in das c-file und wenn nicht, muss die Funktion inline sein +//Als kruskal routine ok, aber es wäre sinnvoll noch eine Instanz-klasse oder so bereitzustellen und dann die als input zu nehmen in einer +//approximate_steiner_tree funktion oder so +Graph kruskal(const Graph &input_graph); + diff --git a/main.cpp b/main.cpp index 1a33923..f6c6375 100644 --- a/main.cpp +++ b/main.cpp @@ -1,14 +1,24 @@ #include #include "STPFileParser.h" -#include "dijkstra/DijkstraGraph.h" +#include "DelaunayGraphFactory.h" +#include "kruskal/kruskal_ind.h" +#include "graph/RectilinearGraph.h" int main() { - std::cout << "Hello, World!" << std::endl; - STPFileParser parser("rect_instance_19.stp"); + STPFileParser parser("rect_instance_20.stp"); + std::cout << std::endl << "---------Start instance----------" << std::endl; Graph graph = parser.create_graph(); - DijkstraGraph dijkstra_graph(graph); - dijkstra_graph.dijkstras_algorithm(0); - graph.print_graph(std::cout); + std::cout << std::endl << "--------Delaunay result----------" << std::endl; + Graph delaunay_graph = DelaunayGraphFactory::create_delaunay_graph(graph); + delaunay_graph.print_graph(std::cout); + std::cout << std::endl << "--------Kruskal result-----------" << std::endl; + Graph kruskal_graph = kruskal(delaunay_graph); + kruskal_graph.print_graph((std::cout)); + std::cout << std::endl << "------Rectilinear result---------" << std::endl; + RectilinearGraph rectilinear_graph(kruskal_graph); + rectilinear_graph.create_graph().print_graph(std::cout); + + return 0; } diff --git a/typedefs.h b/typedefs.h new file mode 100644 index 0000000..93a14ee --- /dev/null +++ b/typedefs.h @@ -0,0 +1,15 @@ +// +// Created by jgier on 12.06.2022. +// + +#ifndef HAUPTAUFGABE_2_TYPEDEFS_H +#define HAUPTAUFGABE_2_TYPEDEFS_H + +#include + +using NodeId = int; +using EdgeId = int; +using WeightType = int; + + +#endif //HAUPTAUFGABE_2_TYPEDEFS_H From 8ec8bbc1a0955581fa56a16d1a19b3c81bd0161e Mon Sep 17 00:00:00 2001 From: Jakob Gierschmann Date: Mon, 27 Jun 2022 21:58:36 +0200 Subject: [PATCH 04/25] added Heap interface, cretaed BinaryHeap, not implemented. --- CMakeLists.txt | 2 +- dijkstra/BinaryHeap.cpp | 25 +++++++++++++++++++++++++ dijkstra/BinaryHeap.h | 21 +++++++++++++++++++++ dijkstra/DijkstraGraph.cpp | 6 +++++- dijkstra/FibonacciHeap.h | 13 +++++++------ dijkstra/Heap.h | 26 ++++++++++++++++++++++++++ dijkstra/StandardHeap.cpp | 2 ++ dijkstra/StandardHeap.h | 13 ++++++++----- 8 files changed, 95 insertions(+), 13 deletions(-) create mode 100644 dijkstra/BinaryHeap.cpp create mode 100644 dijkstra/BinaryHeap.h create mode 100644 dijkstra/Heap.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 42c7fb5..42e41a0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,4 +23,4 @@ add_executable( dijkstra/StandardHeap.h graph/RectilinearGraph.cpp graph/RectilinearGraph.h -) + dijkstra/BinaryHeap.cpp dijkstra/BinaryHeap.h dijkstra/Heap.h) diff --git a/dijkstra/BinaryHeap.cpp b/dijkstra/BinaryHeap.cpp new file mode 100644 index 0000000..257c797 --- /dev/null +++ b/dijkstra/BinaryHeap.cpp @@ -0,0 +1,25 @@ +// +// Created by jgier on 27.06.2022. +// + +#include "BinaryHeap.h" + +void BinaryHeap::insert(Heap::IdType item_id, Heap::KeyType key) { + +} + +int BinaryHeap::extract_min() { + return 0; +} + +void BinaryHeap::decrease_key(int item_id, int new_key) { + +} + +bool BinaryHeap::empty() const { + return false; +} + +BinaryHeap::~BinaryHeap() { + +} diff --git a/dijkstra/BinaryHeap.h b/dijkstra/BinaryHeap.h new file mode 100644 index 0000000..56b8f36 --- /dev/null +++ b/dijkstra/BinaryHeap.h @@ -0,0 +1,21 @@ +// +// Created by jgier on 27.06.2022. +// + +#ifndef HAUPTAUFGABE_2_BINARYHEAP_H +#define HAUPTAUFGABE_2_BINARYHEAP_H + + +#include "Heap.h" + +class BinaryHeap : public Heap { +public: + void insert(IdType item_id, KeyType key) override; + int extract_min() override; + void decrease_key(int item_id, int new_key) override; + bool empty() const override; + ~BinaryHeap() override; +}; + + +#endif //HAUPTAUFGABE_2_BINARYHEAP_H diff --git a/dijkstra/DijkstraGraph.cpp b/dijkstra/DijkstraGraph.cpp index 016607b..26ce7a4 100644 --- a/dijkstra/DijkstraGraph.cpp +++ b/dijkstra/DijkstraGraph.cpp @@ -47,8 +47,12 @@ void DijkstraGraph::dijkstras_algorithm(NodeId root_node_id) { node.included = false; } - StandardHeap candidates; + StandardHeap candidates = StandardHeap{}; // FibonacciHeap candidates; + Heap &candidates_2 = candidates; + Heap *candidates_3 = new StandardHeap{}; + + delete candidates_3; candidates.insert(root_node_id, 0); diff --git a/dijkstra/FibonacciHeap.h b/dijkstra/FibonacciHeap.h index ad29f3d..7843788 100644 --- a/dijkstra/FibonacciHeap.h +++ b/dijkstra/FibonacciHeap.h @@ -8,8 +8,9 @@ #include #include +#include "Heap.h" -class [[maybe_unused]] FibonacciHeap { +class [[maybe_unused]] FibonacciHeap : public Heap { private: struct Node { Node(int item_id, int key); @@ -28,17 +29,17 @@ class [[maybe_unused]] FibonacciHeap { public: FibonacciHeap(); - [[maybe_unused]] void insert(int item_id, int key); + [[maybe_unused]] void insert(int item_id, int key) override; - [[maybe_unused]] int extract_min(); + [[maybe_unused]] int extract_min() override; [[maybe_unused]] [[nodiscard]] int find_min() const; - [[maybe_unused]] void decrease_key(int item_id, int new_key); + [[maybe_unused]] void decrease_key(int item_id, int new_key) override; - [[maybe_unused]] [[nodiscard]] bool empty() const; + [[maybe_unused]] [[nodiscard]] bool empty() const override; - ~FibonacciHeap(); + ~FibonacciHeap() override; private: std::vector _roots; diff --git a/dijkstra/Heap.h b/dijkstra/Heap.h new file mode 100644 index 0000000..ae7b830 --- /dev/null +++ b/dijkstra/Heap.h @@ -0,0 +1,26 @@ +// +// Created by jgier on 27.06.2022. +// + +#ifndef HAUPTAUFGABE_2_HEAP_H +#define HAUPTAUFGABE_2_HEAP_H + + +class Heap { +public: + using IdType = int; + using KeyType = int; + + virtual void insert(IdType item_id, KeyType key) = 0; + + virtual int extract_min() = 0; + + virtual void decrease_key(int item_id, int new_key) = 0; + + [[nodiscard]] virtual bool empty() const = 0; + + virtual ~Heap() = 0; +}; + + +#endif //HAUPTAUFGABE_2_HEAP_H diff --git a/dijkstra/StandardHeap.cpp b/dijkstra/StandardHeap.cpp index ed9a7bd..cae5aff 100644 --- a/dijkstra/StandardHeap.cpp +++ b/dijkstra/StandardHeap.cpp @@ -30,6 +30,8 @@ bool StandardHeap::empty() const { return _heap.empty(); } +StandardHeap::~StandardHeap() = default; + bool StandardHeap::Node::operator<(const StandardHeap::Node &other) const { return other.key < key; } diff --git a/dijkstra/StandardHeap.h b/dijkstra/StandardHeap.h index 4eaf469..e9852a8 100644 --- a/dijkstra/StandardHeap.h +++ b/dijkstra/StandardHeap.h @@ -7,8 +7,9 @@ #include +#include "Heap.h" -class StandardHeap { +class StandardHeap : public Heap { private: struct Node { int item_id, key; @@ -16,10 +17,12 @@ class StandardHeap { }; public: - void insert(int item_id, int key); - int extract_min(); - void decrease_key(int item_id, int new_key); - [[nodiscard]] bool empty() const; + ~StandardHeap() override; +public: + void insert(int item_id, int key) override; + int extract_min() override; + void decrease_key(int item_id, int new_key) override; + [[nodiscard]] bool empty() const override; private: std::vector _heap; }; From 64ec9ed97b6e924467655d3fee52e702d69ab3ad Mon Sep 17 00:00:00 2001 From: Jakob Gierschmann Date: Mon, 27 Jun 2022 22:01:55 +0200 Subject: [PATCH 05/25] added postscript output --- graph/RectilinearGraph.cpp | 45 ++++++++++++++++++++++++++++++++++++++ graph/RectilinearGraph.h | 17 ++++++++++++++ main.cpp | 24 +++++++++++++------- 3 files changed, 78 insertions(+), 8 deletions(-) diff --git a/graph/RectilinearGraph.cpp b/graph/RectilinearGraph.cpp index d5870ff..d27f2ee 100644 --- a/graph/RectilinearGraph.cpp +++ b/graph/RectilinearGraph.cpp @@ -3,6 +3,8 @@ // #include "RectilinearGraph.h" +#include +#include RectilinearGraph::RectilinearGraph(NodeId num_nodes) { auto const sqrt = static_cast(std::sqrt(num_nodes)); @@ -96,3 +98,46 @@ Graph RectilinearGraph::create_graph() const { return result; } +void RectilinearGraph::print_as_postscript(std::ostream &os, const std::string &base_file_name) { + std::ifstream base_file(base_file_name); + assert(base_file); + std::string line; + + os << base_file.rdbuf(); + base_file.close(); + + os << std::endl; + os << "%%BeginSetup" << std::endl << std::endl; + os << 0 << " " << _grid_width + 1 << " " << 0 << " " << _grid_width + 1 << " SetAxes" << std::endl << std::endl; + + os << _terminals.size() << " DefineTerminals" << std::endl; + for (auto terminal_id: _terminals) { + os << "\t" << x_coord(terminal_id) << "\t" << y_coord(terminal_id) << "\tDT" << std::endl; + } + + os << std::endl << "%%EndSetup" << std::endl << std::endl; + + os << "%%Page: 1 1" << std::endl; + os << "BeginPlot" << std::endl; + os << "\tPlot_Terminals" << std::endl; + for (int i = 0; i < num_nodes(); i++) { + if (_vertical_edges_above_node[i]) { + os << "\t" << x_coord(i) << "\t" << y_coord(i) << "\t" << x_coord(i) << "\t" << y_coord(i) + 1 << "\tS" + << std::endl; + } + if (_horizontal_edges_after_node[i]) { + os << "\t" << x_coord(i) << "\t" << y_coord(i) << "\t" << x_coord(i) + 1 << "\t" << y_coord(i) << "\tS" + << std::endl; + } + } + os << " (Steiner Minimal Tree: " << _terminals.size() << "points, length=" << num_edges() << ")" << std::endl; + os << "EndPlot" << std::endl; +} + +int RectilinearGraph::x_coord(NodeId node) const { + return node % _grid_width + 1; +} + +int RectilinearGraph::y_coord(NodeId node) const { + return static_cast(node / _grid_width) + 1; +} diff --git a/graph/RectilinearGraph.h b/graph/RectilinearGraph.h index a9466a0..5e1deea 100644 --- a/graph/RectilinearGraph.h +++ b/graph/RectilinearGraph.h @@ -24,8 +24,21 @@ class RectilinearGraph { void add_any_edge(NodeId node_a, NodeId node_b, bool flipped = true); + int num_edges() { + int ret = 0; + for (auto b: _horizontal_edges_after_node) { + if (b) ret += 1; + } + for (auto b: _vertical_edges_above_node) { + if (b) ret += 1; + } + return ret; + } + [[nodiscard]] Graph create_graph() const; + void print_as_postscript(std::ostream &os, const std::string &base_file_name); + private: NodeId _grid_width; std::vector _terminals; @@ -41,6 +54,10 @@ class RectilinearGraph { [[nodiscard]] bool validate_node(NodeId node) const; static void swap_if_a_larger(NodeId &node_a, NodeId &node_b); + + int x_coord(NodeId node) const; + + int y_coord(NodeId node) const; }; diff --git a/main.cpp b/main.cpp index f6c6375..1844943 100644 --- a/main.cpp +++ b/main.cpp @@ -4,20 +4,28 @@ #include "kruskal/kruskal_ind.h" #include "graph/RectilinearGraph.h" -int main() { +int main(int argc, char *argv[]) { STPFileParser parser("rect_instance_20.stp"); - std::cout << std::endl << "---------Start instance----------" << std::endl; + std::cout << std::endl << "--------Start instance-----------" << std::endl; Graph graph = parser.create_graph(); - graph.print_graph(std::cout); + //graph.print_graph(std::cout); std::cout << std::endl << "--------Delaunay result----------" << std::endl; Graph delaunay_graph = DelaunayGraphFactory::create_delaunay_graph(graph); - delaunay_graph.print_graph(std::cout); - std::cout << std::endl << "--------Kruskal result-----------" << std::endl; + //delaunay_graph.print_graph(std::cout); + std::cout << std::endl << "---------Kruskal result----------" << std::endl; Graph kruskal_graph = kruskal(delaunay_graph); - kruskal_graph.print_graph((std::cout)); - std::cout << std::endl << "------Rectilinear result---------" << std::endl; + //kruskal_graph.print_graph((std::cout)); + std::cout << std::endl << "-------Rectilinear result--------" << std::endl; RectilinearGraph rectilinear_graph(kruskal_graph); - rectilinear_graph.create_graph().print_graph(std::cout); + auto graph_2 = rectilinear_graph.create_graph(); + //graph_2.print_graph(std::cout); + std::cout << std::endl << "-------2nd Kruskal result--------" << std::endl; + auto kruskal_graph_2 = kruskal(graph_2); + kruskal_graph_2.print_graph(std::cout); + + std::cout << std::endl << "----------PostScript-------------" << std::endl; + RectilinearGraph output_graph(kruskal_graph_2); + output_graph.print_as_postscript(std::cout, "../PostScript/template.ps"); return 0; From 2e38ce6c6f1800cb7f884767e5e8f37f82bde3bc Mon Sep 17 00:00:00 2001 From: Jakob Gierschmann Date: Mon, 27 Jun 2022 22:02:47 +0200 Subject: [PATCH 06/25] added rudimentary support for DICMAS instances --- .gitignore | 1 + PostScript/template.ps | Bin 0 -> 5319 bytes STPFileParser.cpp | 55 ++++++++++++++++++++++++++++++++++------- STPFileParser.h | 5 ++++ 4 files changed, 52 insertions(+), 9 deletions(-) create mode 100644 PostScript/template.ps diff --git a/.gitignore b/.gitignore index 389a4fd..d2b89dc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ /cmake-build-debug/ +/build/ /4_filtered/ /.vscode/ /.idea/ diff --git a/PostScript/template.ps b/PostScript/template.ps new file mode 100644 index 0000000000000000000000000000000000000000..b15d794c6ce14b40f8823b87d1c9511202353d9e GIT binary patch literal 5319 zcmeHLOK;pZ5I#%4VtO!|0P(&YH;I81XzZpf;1ovIZM~;LE77||wW3N%_BuxXd%qcq z6nAYmKrii~Ic!AEa5#@|9`wOi7xTmK3bRy)N25u4aByl>)*5?4^H%9fS-P}J(XYxj z+EjEjnlg{6@2p;}+Y<_(^elf)(>HHkzo6MC%=$m8K>pZJgUA9u^>PD3(#A=+G zyxXX%{aL9(6(3b&I-9HJgsx51(xu+0=J2E0WL3mF@9MgQ3K4g0P2Q;18_f1z@D|NR z6XL7CuZmN%;hP$)-l>(Yz)+f1w94lS-spX{FJgS2Eme7`>_%5v*(8%BDb#gxKzwaw zs>4sp8fp!d%tx(fOvk#)*K|*>X0+)_`b;>MjxVxW+23^0u9MfJV`3hb*AsqJ9GPS~ zbp%Iwr`fG@{YfRS;{Ew1D@#wAJx1xp?J&w^e#a@|S8|2WA+V&hgq!KS4Lbf&JcMeSNqUh29ApveL< zNFD@u=_tBtbfHKUD@Ep7n3rZdO4IQv&OC4%g=?wlmJS5W=rkFQ2xV6XHouK@Xn?bI z)>3UW;4Hpqk&xJ7jn%fb3TZ>Px-20`ZL_>B?`Wd{RAGoT-ZO7R^z5T5x?FL3GqcAr z1X=MY!3in@qS^Y<0}V^9q% z4sH+`rT`a6n+f|%ctnV9|A0g8@z&lNDSZf^k_b}QsFU;{IfUaCNS3N@vs9Mx5C;fl zE#j#`7sxOR7iyNVh7Md5qz8kgd%hm>== z$93=uZ!|9ItU2t(#uWuhW^HySDe6dzr%69mh1$|Yn0wpKF8Ll7xeymPmH|0+2dmwU z{QD?bG;)-IXG&R7#5bXneYZsoxJQG6gbs*c`!7wDzA~sagLeT0Amn4hV zoPA;48+36wZyAZnbViN)l7MqQ{?6(J=k9h-xJCWRzJPjXx~WSzwe{>T&`wpTg zM;Ko+n*;4Ar%Cdeb1+RlrwRM^5#?`nyZ)!@dw)`X<59>4V#sLi7ffW1N6i%XI4a`) zl*wGlzGQl`F*gbgFmlh4lufRGl|qkn3@m3i<$0%Bn_K+v{YUy*m5oY$4Xo%=`ukNy zi|Pe&^m1b?hDntFMmlT{5UNI~VvHW#>+?mB=|k>w%x{KE&TMu|8F9nM+_vIz5VGbh zD4panbV`&xc>H|7RD#Wr;^p%DcwzJJEJ?qEhrDD5BqfZtzTku=9ypabgJW>PQ{TN z=6gWkgMrP{3TV8g$@w zjhCxeRNxvAD*8H%j$XOP9PPYJFdc485%yfL`e&cCZhjd$H&Kvq6!_(J0~wk}RyM;6 z@3HrUq}bVsKjQ8VpoT=m#uFbTk0ZMw>;IJb*Hfl1mPiwc80QM!AOE#v*?pA> specifier >> val_1 >> val_2 >> val_3; if (specifier == "Nodes") { @@ -85,7 +96,6 @@ void STPFileParser::read_graph_from_file(std::istream &file) { _edges.emplace_back(val_1 - 1, val_2 - 1, val_3); } } - if ((size_t) num_edges != _edges.size()) { throw std::invalid_argument("Invalid file format: Wrong number of edges."); } @@ -96,23 +106,44 @@ void STPFileParser::read_terminals_from_file(std::istream &file) { std::string line; std::string specifier; int val; - int num_terminals; while (line != "END") { - if (!std::getline(file, line) or line == "EOF") { + if (!std::getline(file, line) or line == "EOF" or line == "EOF\r") { throw std::invalid_argument("Invalid file format."); } + remove_carriage_return(line); std::stringstream ss(line); ss >> specifier >> val; if (specifier == "Terminals") { _terminals.reserve(val); - num_terminals = val; + _num_terminals = val; } else if (specifier == "T") { // subtract 1 from node_id, this program is 0-based _terminals.push_back(val - 1); } } - if ((size_t) num_terminals != _terminals.size()) { + if ((size_t) _num_terminals != _terminals.size()) { + throw std::invalid_argument("Invalid file format. Wrong number of terminals."); + } +} + +void STPFileParser::read_coordinates_from_file(std::istream &file) { + std::string line; + std::string specifier; + int terminal_id, x_coord, y_coord; + while (line != "END") { + if (!std::getline(file, line) or line == "EOF" or line == "EOF\r") { + throw std::invalid_argument("Invalid file format."); + } + remove_carriage_return(line); + std::stringstream ss(line); + ss >> specifier >> terminal_id >> x_coord >> y_coord; + if (specifier == "DD") { + // TODO where do the coordiantes begin, at 0,0 or 1,1? + _terminals.push_back((y_coord /* -1 */) * 10000 + x_coord /* -1 */ ); + } + } + if ((size_t) _num_terminals != _terminals.size()) { throw std::invalid_argument("Invalid file format. Wrong number of terminals."); } } @@ -127,4 +158,10 @@ std::ifstream STPFileParser::open_input_file() const { return file; } +void STPFileParser::remove_carriage_return(std::string &s) { + while (s[s.size() - 1] == '\r') { + s = s.erase(s.size() - 1); + } +} + diff --git a/STPFileParser.h b/STPFileParser.h index b3053f8..d98cea6 100644 --- a/STPFileParser.h +++ b/STPFileParser.h @@ -23,8 +23,13 @@ class STPFileParser { void read_terminals_from_file(std::istream &file); + void read_coordinates_from_file(std::istream &file); + + static void remove_carriage_return(std::string &s); + std::string _filename; NodeId _num_nodes; + NodeId _num_terminals; std::vector _terminals; std::vector _edges; }; From c9643d7928865ed4aa62ffaf9df63b17e3d09b94 Mon Sep 17 00:00:00 2001 From: JakobGie <44549596+armytrong@users.noreply.github.com> Date: Mon, 27 Jun 2022 22:06:05 +0200 Subject: [PATCH 07/25] Create cmake.yml --- .github/workflows/cmake.yml | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 .github/workflows/cmake.yml diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml new file mode 100644 index 0000000..efc0483 --- /dev/null +++ b/.github/workflows/cmake.yml @@ -0,0 +1,37 @@ +name: CMake + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + +env: + # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) + BUILD_TYPE: Release + +jobs: + build: + # The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac. + # You can convert this to a matrix build if you need cross-platform coverage. + # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Configure CMake + # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. + # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type + run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} + + - name: Build + # Build your program with the given configuration + run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} + + - name: Test + working-directory: ${{github.workspace}}/build + # Execute tests defined by the CMake configuration. + # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail + run: ctest -C ${{env.BUILD_TYPE}} + From 5dc1fa9fba81ae067a4513faa8cc963682e40410 Mon Sep 17 00:00:00 2001 From: JakobGie <44549596+armytrong@users.noreply.github.com> Date: Mon, 27 Jun 2022 22:11:10 +0200 Subject: [PATCH 08/25] Update cmake.yml --- .github/workflows/cmake.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index efc0483..665b582 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -9,6 +9,8 @@ on: env: # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) BUILD_TYPE: Release + CC: gcc-10 + CXX: g++-10 jobs: build: From 6787fd1075ae9c1cc7483df82a0d19494540759b Mon Sep 17 00:00:00 2001 From: Jakob Gierschmann Date: Mon, 27 Jun 2022 22:17:52 +0200 Subject: [PATCH 09/25] fixed mistake in Heap that lead to linker error --- dijkstra/Heap.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dijkstra/Heap.h b/dijkstra/Heap.h index ae7b830..1715b97 100644 --- a/dijkstra/Heap.h +++ b/dijkstra/Heap.h @@ -19,7 +19,7 @@ class Heap { [[nodiscard]] virtual bool empty() const = 0; - virtual ~Heap() = 0; + virtual ~Heap() = default; }; From 6505fe3ceb7e79d12bb7b6156595d9d78518bbaf Mon Sep 17 00:00:00 2001 From: Jakob Gierschmann Date: Wed, 29 Jun 2022 00:27:04 +0200 Subject: [PATCH 10/25] implemented Dalunay triangulation eficciently, other refactorings --- .gitignore | 1 + .gitmodules | 3 + CMakeLists.txt | 20 +- ...ry.cpp => MehlhornDelaunayGraphFactory.cpp | 12 +- ...actory.h => MehlhornDelaunayGraphFactory.h | 8 +- STPFileParser.cpp | 11 ++ STPFileParser.h | 2 + delaunay/DelaunayGraph.cpp | 177 ++++++++++++++++++ delaunay/DelaunayGraph.h | 66 +++++++ delaunay/DelaunayPriorityQueue.cpp | 36 ++++ delaunay/DelaunayPriorityQueue.h | 46 +++++ delaunay/DelaunaySet.cpp | 29 +++ delaunay/DelaunaySet.h | 33 ++++ dijkstra/DijkstraGraph.cpp | 4 +- googletest | 1 + {dijkstra => heap}/BinaryHeap.cpp | 0 {dijkstra => heap}/BinaryHeap.h | 0 {dijkstra => heap}/FibonacciHeap.cpp | 0 {dijkstra => heap}/FibonacciHeap.h | 0 {dijkstra => heap}/Heap.h | 0 {dijkstra => heap}/StandardHeap.cpp | 0 {dijkstra => heap}/StandardHeap.h | 0 main.cpp | 58 ++++-- typedefs.h | 1 + 24 files changed, 467 insertions(+), 41 deletions(-) create mode 100644 .gitmodules rename DelaunayGraphFactory.cpp => MehlhornDelaunayGraphFactory.cpp (88%) rename DelaunayGraphFactory.h => MehlhornDelaunayGraphFactory.h (81%) create mode 100644 delaunay/DelaunayGraph.cpp create mode 100644 delaunay/DelaunayGraph.h create mode 100644 delaunay/DelaunayPriorityQueue.cpp create mode 100644 delaunay/DelaunayPriorityQueue.h create mode 100644 delaunay/DelaunaySet.cpp create mode 100644 delaunay/DelaunaySet.h create mode 160000 googletest rename {dijkstra => heap}/BinaryHeap.cpp (100%) rename {dijkstra => heap}/BinaryHeap.h (100%) rename {dijkstra => heap}/FibonacciHeap.cpp (100%) rename {dijkstra => heap}/FibonacciHeap.h (100%) rename {dijkstra => heap}/Heap.h (100%) rename {dijkstra => heap}/StandardHeap.cpp (100%) rename {dijkstra => heap}/StandardHeap.h (100%) diff --git a/.gitignore b/.gitignore index d2b89dc..2a5c48e 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ /4_filtered/ /.vscode/ /.idea/ +/PostScript/test.ps diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..09d09ff --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "googletest"] + path = googletest + url = git@github.com:google/googletest.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 42e41a0..7b2f462 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,8 +3,7 @@ project(Hauptaufgabe_2) set(CMAKE_CXX_STANDARD 20) -add_executable( - Hauptaufgabe_2 +add_executable(Hauptaufgabe_2 main.cpp SteinerTreeApproximator.cpp SteinerTreeApproximator.h @@ -12,15 +11,18 @@ add_executable( dijkstra/DijkstraGraph.cpp dijkstra/DijkstraGraph.h graph/Graph.cpp graph/Graph.h - dijkstra/FibonacciHeap.cpp - dijkstra/FibonacciHeap.h + heap/FibonacciHeap.cpp + heap/FibonacciHeap.h typedefs.h - DelaunayGraphFactory.cpp - DelaunayGraphFactory.h + MehlhornDelaunayGraphFactory.cpp + MehlhornDelaunayGraphFactory.h kruskal/kruskal_ind.cpp kruskal/kruskal_ind.h - dijkstra/StandardHeap.cpp - dijkstra/StandardHeap.h + heap/StandardHeap.cpp + heap/StandardHeap.h graph/RectilinearGraph.cpp graph/RectilinearGraph.h - dijkstra/BinaryHeap.cpp dijkstra/BinaryHeap.h dijkstra/Heap.h) + heap/BinaryHeap.cpp heap/BinaryHeap.h heap/Heap.h googletest/MainTest.cpp delaunay/DelaunayGraph.cpp delaunay/DelaunayGraph.h delaunay/DelaunaySet.cpp delaunay/DelaunaySet.h delaunay/DelaunayPriorityQueue.cpp delaunay/DelaunayPriorityQueue.h) + + +add_subdirectory(googletest) diff --git a/DelaunayGraphFactory.cpp b/MehlhornDelaunayGraphFactory.cpp similarity index 88% rename from DelaunayGraphFactory.cpp rename to MehlhornDelaunayGraphFactory.cpp index 8a518f5..b27c1cd 100644 --- a/DelaunayGraphFactory.cpp +++ b/MehlhornDelaunayGraphFactory.cpp @@ -4,12 +4,12 @@ #include #include -#include "DelaunayGraphFactory.h" +#include "MehlhornDelaunayGraphFactory.h" void -DelaunayGraphFactory::bucket_sort( +MehlhornDelaunayGraphFactory::bucket_sort( std::vector &vector, - DelaunayGraphFactory::TypeToNodeId type_to_node_id, + MehlhornDelaunayGraphFactory::TypeToNodeId type_to_node_id, NodeId max_id) { std::vector> bucket_list; bucket_list.resize(max_id); @@ -25,7 +25,7 @@ DelaunayGraphFactory::bucket_sort( } } -Graph DelaunayGraphFactory::create_delaunay_graph( +Graph MehlhornDelaunayGraphFactory::create_delaunay_graph( DijkstraGraph const &dijkstra_graph, Graph const &graph ) { @@ -83,14 +83,14 @@ Graph DelaunayGraphFactory::create_delaunay_graph( return {graph.num_nodes(), graph.terminals(), edges}; } -Graph DelaunayGraphFactory::create_delaunay_graph(const Graph &graph) { +Graph MehlhornDelaunayGraphFactory::create_delaunay_graph(const Graph &graph) { DijkstraGraph dijkstra_graph(graph); NodeId root_node = dijkstra_graph.add_node(graph.terminals(), std::vector(graph.terminals().size(), 0)); dijkstra_graph.dijkstras_algorithm(root_node); return create_delaunay_graph(dijkstra_graph, graph); } -DelaunayGraphFactory::CandidateEdge::CandidateEdge(NodeId terminal_1, NodeId terminal_2, WeightType weight) : +MehlhornDelaunayGraphFactory::CandidateEdge::CandidateEdge(NodeId terminal_1, NodeId terminal_2, WeightType weight) : smaller_terminal(std::min(terminal_1, terminal_2)), larger_terminal(std::max(terminal_1, terminal_2)), weight(weight) {} diff --git a/DelaunayGraphFactory.h b/MehlhornDelaunayGraphFactory.h similarity index 81% rename from DelaunayGraphFactory.h rename to MehlhornDelaunayGraphFactory.h index 7b3cfb7..acc2ebe 100644 --- a/DelaunayGraphFactory.h +++ b/MehlhornDelaunayGraphFactory.h @@ -2,14 +2,14 @@ // Created by jgier on 20.06.2022. // -#ifndef HAUPTAUFGABE_2_DELAUNAYGRAPHFACTORY_H -#define HAUPTAUFGABE_2_DELAUNAYGRAPHFACTORY_H +#ifndef HAUPTAUFGABE_2_MEHLHORNDELAUNAYGRAPHFACTORY_H +#define HAUPTAUFGABE_2_MEHLHORNDELAUNAYGRAPHFACTORY_H #include "graph/Graph.h" #include "dijkstra/DijkstraGraph.h" -class DelaunayGraphFactory { +class MehlhornDelaunayGraphFactory { public: @@ -36,4 +36,4 @@ class DelaunayGraphFactory { }; -#endif //HAUPTAUFGABE_2_DELAUNAYGRAPHFACTORY_H +#endif //HAUPTAUFGABE_2_MEHLHORNDELAUNAYGRAPHFACTORY_H diff --git a/STPFileParser.cpp b/STPFileParser.cpp index 310e2c1..24d16da 100644 --- a/STPFileParser.cpp +++ b/STPFileParser.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include "STPFileParser.h" constexpr char FILE_HEADER[] = "33D32945 STP File, STP Format Version 1.0"; @@ -164,4 +165,14 @@ void STPFileParser::remove_carriage_return(std::string &s) { } } +DelaunayGraph STPFileParser::create_delaunay_graph() { + DelaunayGraph delaunay_graph; + auto sqrt = static_cast(std::sqrt(_num_nodes)); + assert(sqrt * sqrt == _num_nodes); + for (auto terminal_id: _terminals) { + delaunay_graph.add_terminal(terminal_id % sqrt, static_cast(terminal_id / sqrt), terminal_id); + } + return delaunay_graph; +} + diff --git a/STPFileParser.h b/STPFileParser.h index d98cea6..570569c 100644 --- a/STPFileParser.h +++ b/STPFileParser.h @@ -8,12 +8,14 @@ #include #include "graph/Graph.h" +#include "delaunay/DelaunayGraph.h" class STPFileParser { public: explicit STPFileParser(std::string filename); Graph create_graph(); + DelaunayGraph create_delaunay_graph(); private: diff --git a/delaunay/DelaunayGraph.cpp b/delaunay/DelaunayGraph.cpp new file mode 100644 index 0000000..2b0aa9c --- /dev/null +++ b/delaunay/DelaunayGraph.cpp @@ -0,0 +1,177 @@ +// +// Created by jgier on 27.06.2022. +// + +#include +#include "DelaunayGraph.h" +#include "../heap/BinaryHeap.h" +#include "DelaunayPriorityQueue.h" +#include "DelaunaySet.h" +#include +#include +#include + +void DelaunayGraph::add_terminal(GridUnit x_coord, GridUnit y_coord, NodeId id) { + _terminals.emplace_back(x_coord, y_coord, id); + _max_x = std::max(_max_x, std::abs(x_coord)); + _max_y = std::max(_max_y, std::abs(y_coord)); + +} + +void DelaunayGraph::calculate() { + DelaunayPriorityQueue X(_max_x, _max_y); + for (auto const &terminal: _terminals) { + X.insert(terminal, terminal.x_coord, ACTIVE); + } + DelaunaySet Y; + Y.insert({-_max_x - 1, -_max_y - 1}); + Y.insert({-_max_x - 1, _max_y + 1}); + + std::map ages; + int age_counter = 0; + + while (not X.empty()) { + auto P = X.extract_min(); + ages.insert(std::make_pair(P.terminal, age_counter)); + age_counter++; + if (P.status == ACTIVE) { + Y.insert(P.terminal); + auto sucessor = Y.sucessor(P.terminal); + auto predecessor = Y.predecessor((P.terminal)); + assert(sucessor.has_value() and predecessor.has_value()); + if (sucessor->x_coord != -_max_x - 1) { + _edges.push_back({P.terminal, *sucessor}); + update_inactivation_records(X, Y, ages[P.terminal] >= ages[*sucessor] ? *sucessor : P.terminal); + } + if (predecessor->x_coord != -_max_x - 1) { + _edges.push_back({P.terminal, *predecessor}); + update_inactivation_records(X, Y, ages[P.terminal] >= ages[*predecessor] ? *predecessor : P.terminal); + } + } else { + auto q = Y.predecessor(P.terminal); + Y.del(P.terminal); + auto sucessor = Y.sucessor(*q); + if (q->x_coord != -_max_x - 1 and sucessor->x_coord != -_max_x - 1) _edges.push_back({*q, *sucessor}); + update_inactivation_records(X, Y, ages[*q] >= ages[*sucessor] ? *sucessor : *q); + } + } +} + +void DelaunayGraph::update_inactivation_records(DelaunayPriorityQueue &X, const DelaunaySet &Y, + DelaunayGraph::Terminal terminal) { + auto r = Y.predecessor(terminal); + auto q = Y.sucessor(terminal); + if (r->x_coord > terminal.x_coord and q->x_coord >= terminal.x_coord) { + if (X.find_inactivation_record(terminal).has_value()) { + X.change_priority(terminal, terminal.x_coord + q->y_coord - r->y_coord); + } else { + X.insert(terminal, terminal.x_coord + q->y_coord - r->y_coord, INACTIVE); + } + } +} + +void DelaunayGraph::primitive_print(std::ostream &os) { + os << "Terminals: " << std::endl; + for (auto const &terminal: _terminals) { + os << "id: " << terminal.id << ", x: " << terminal.x_coord << ", y: " << terminal.y_coord << std::endl; + } + os << std::endl << "Edges: " << std::endl; + for (auto const &edge: _edges) { + os << edge.terminal_a.x_coord << " " << edge.terminal_a.y_coord << " -- " << edge.terminal_b.x_coord << " " + << edge.terminal_b.y_coord << std::endl; + } +} + +Graph DelaunayGraph::export_graph() { + NodeId sqrt = std::max(_max_x, _max_y) + 1; + NodeId num_nodes = sqrt * sqrt; + std::vector terminals; + for (auto const &terminal: _terminals) { + terminals.push_back(terminal.x_coord + terminal.y_coord * sqrt); + } + Graph ret{num_nodes, terminals}; + for (auto const &edge: _edges) { + ret.add_edge({ + edge.terminal_a.x_coord + edge.terminal_a.y_coord * sqrt, + edge.terminal_b.x_coord + edge.terminal_b.y_coord * sqrt, + std::abs(edge.terminal_a.x_coord - edge.terminal_b.x_coord) + + std::abs(edge.terminal_a.y_coord - edge.terminal_b.y_coord) + }); + } + + return ret; +} + +void DelaunayGraph::translate_terminal_from_1_to_infty_norm(DelaunayGraph::Terminal &t) { + GridUnit new_x = t.x_coord + t.y_coord; + GridUnit new_y = t.x_coord - t.y_coord; + t.x_coord = new_x; + t.y_coord = new_y; +} + +void DelaunayGraph::translate_terminal_from_infty_to_1_norm(DelaunayGraph::Terminal &t) { + GridUnit new_x = (t.x_coord + t.y_coord) / 2; + GridUnit new_y = t.x_coord - new_x; + t.x_coord = new_x; + t.y_coord = new_y; +} + +void DelaunayGraph::translate_from_1_to_infty_norm() { + for (auto &terminal: _terminals) { + translate_terminal_from_1_to_infty_norm(terminal); + } + for (auto &edge: _edges) { + translate_terminal_from_1_to_infty_norm(edge.terminal_a); + translate_terminal_from_1_to_infty_norm(edge.terminal_b); + } +} + +void DelaunayGraph::translate_from_infty_to_1_norm() { + for (auto &terminal: _terminals) { + translate_terminal_from_infty_to_1_norm(terminal); + } + for (auto &edge: _edges) { + translate_terminal_from_infty_to_1_norm(edge.terminal_a); + translate_terminal_from_infty_to_1_norm(edge.terminal_b); + } +} + +void DelaunayGraph::print_as_postscript(std::ostream &os, const std::string &base_file_name) { + std::ifstream base_file(base_file_name); + assert(base_file); + std::string line; + + os << base_file.rdbuf(); + base_file.close(); + + GridUnit grid_width = static_cast(std::sqrt(std::max(_max_x, _max_y))); + + os << std::endl; + os << "%%BeginSetup" << std::endl << std::endl; + os << 0 << " " << (grid_width + 1) * (grid_width + 1) << " " << 0 << " " << (grid_width + 1) * (grid_width + 1) + << " SetAxes" + << + std::endl << + std::endl; + + os << _terminals.size() << " DefineTerminals" << std::endl; + for (auto terminal: _terminals) { + os << "\t" << terminal.x_coord + 1 << "\t" << terminal.y_coord + 1 << "\tDT" << std::endl; + } + + os << std::endl << "%%EndSetup" << std::endl << std::endl; + + os << "%%Page: 1 1" << std::endl; + os << "BeginPlot" << std::endl; + os << "\tPlot_Terminals" << std::endl; + for (auto const &edge: _edges) { + os << "\t" << edge.terminal_a.x_coord + 1 + << "\t" << edge.terminal_a.y_coord + 1 + << "\t" << edge.terminal_b.x_coord + 1 + << "\t" << edge.terminal_b.y_coord + 1 << "\tS" << std::endl; + } + + //os << " (Steiner Minimal Tree: " << _terminals.size() << "points, length=" << grid_width * grid_widht << ")" << + //std::endl; + os << "EndPlot" << std::endl; +} diff --git a/delaunay/DelaunayGraph.h b/delaunay/DelaunayGraph.h new file mode 100644 index 0000000..87fb17a --- /dev/null +++ b/delaunay/DelaunayGraph.h @@ -0,0 +1,66 @@ +// +// Created by jgier on 27.06.2022. +// + +#ifndef HAUPTAUFGABE_2_DELAUNAYGRAPH_H +#define HAUPTAUFGABE_2_DELAUNAYGRAPH_H + + +#include +#include "../typedefs.h" +#include "../graph/Graph.h" + + +class DelaunayPriorityQueue; + +class DelaunaySet; + + +class DelaunayGraph { +public: + DelaunayGraph() : _max_x(0), _max_y(0) {} + + struct Terminal { + Terminal(GridUnit x_coord, GridUnit y_coord, NodeId id = -1) : x_coord(x_coord), y_coord(y_coord), id(id) {} + + GridUnit x_coord; + GridUnit y_coord; + NodeId id; + + bool operator==(Terminal const &other) const { return x_coord == other.x_coord and y_coord == other.y_coord; } + + bool operator<(Terminal const &other) const { + return x_coord < other.x_coord or (x_coord == other.x_coord and y_coord < other.y_coord); + } + }; + + struct Edge { + Terminal terminal_a; + Terminal terminal_b; + }; + + void add_terminal(GridUnit x_coord, GridUnit y_coord, NodeId id = -1); + void calculate(); + void primitive_print(std::ostream &os); + + void translate_from_1_to_infty_norm(); + void translate_from_infty_to_1_norm(); + + + Graph export_graph(); + + void print_as_postscript(std::ostream &os, const std::string &base_file_name); + +private: + + static void update_inactivation_records(DelaunayPriorityQueue &X, DelaunaySet const &Y, Terminal terminal); + void translate_terminal_from_1_to_infty_norm(Terminal &t); + void translate_terminal_from_infty_to_1_norm(Terminal &t); + + std::vector _terminals; + std::vector _edges; + GridUnit _max_x, _max_y; +}; + + +#endif //HAUPTAUFGABE_2_DELAUNAYGRAPH_H diff --git a/delaunay/DelaunayPriorityQueue.cpp b/delaunay/DelaunayPriorityQueue.cpp new file mode 100644 index 0000000..2bb30c2 --- /dev/null +++ b/delaunay/DelaunayPriorityQueue.cpp @@ -0,0 +1,36 @@ +// +// Created by jgier on 28.06.2022. +// + +#include +#include "DelaunayPriorityQueue.h" + + +DelaunayPriorityQueue::Record DelaunayPriorityQueue::extract_min() { + auto ret = *_set.lower_bound(MINIMUM_RECORD); + assert(_set.contains(ret)); + _set.erase(ret); + return ret; +} + +void DelaunayPriorityQueue::insert(const DelaunayPriorityQueue::Terminal &t, GridUnit trans, ActiveInactive status) { + //assert(t.x_coord >= 0 and t.y_coord >= 0 and trans >= 0); + _set.insert({t, trans, status}); +} + +std::optional DelaunayPriorityQueue::find_inactivation_record + (DelaunayPriorityQueue::Terminal t) const { + auto record = *_set.lower_bound({t, 0, INACTIVE}); + return (record.terminal == t and record.status == INACTIVE) ? std::make_optional(record) : std::nullopt; +} + +void DelaunayPriorityQueue::change_priority(const DelaunayPriorityQueue::Terminal &t, GridUnit new_trans) { + auto record = *_set.lower_bound({t, 0, INACTIVE}); + assert(record.status == INACTIVE); + _set.erase(record); + _set.insert({t, new_trans, INACTIVE}); +} + +bool DelaunayPriorityQueue::empty() const { + return _set.empty(); +} diff --git a/delaunay/DelaunayPriorityQueue.h b/delaunay/DelaunayPriorityQueue.h new file mode 100644 index 0000000..d693c05 --- /dev/null +++ b/delaunay/DelaunayPriorityQueue.h @@ -0,0 +1,46 @@ +// +// Created by jgier on 28.06.2022. +// + +#ifndef HAUPTAUFGABE_2_DELAUNAYPRIORITYQUEUE_H +#define HAUPTAUFGABE_2_DELAUNAYPRIORITYQUEUE_H + + +#include +#include "DelaunayGraph.h" + +enum ActiveInactive { + INACTIVE, ACTIVE +}; + + +class DelaunayPriorityQueue { +public: + DelaunayPriorityQueue(GridUnit max_x, GridUnit max_y) : MINIMUM_RECORD( + {{-max_x, -max_y}, -max_x - max_y, INACTIVE}) {} + + using Terminal = DelaunayGraph::Terminal; + struct Record { + Terminal terminal; + GridUnit trans; + ActiveInactive status; + }; + + Record extract_min(); + void insert(Terminal const &t, GridUnit trans, ActiveInactive status); + std::optional find_inactivation_record(Terminal t) const; + void change_priority(Terminal const &t, GridUnit new_trans); + bool empty() const; + +private: + static constexpr auto greater_fun = [](Record const &rhs, Record const &lhs) { + return (lhs.trans > rhs.trans) or (lhs.trans == rhs.trans and lhs.terminal.y_coord > rhs.terminal.y_coord) + or (lhs.trans == rhs.trans and lhs.terminal.y_coord == rhs.terminal.y_coord and lhs.status < rhs.status); + }; + std::set _set; + + const DelaunayPriorityQueue::Record MINIMUM_RECORD; +}; + + +#endif //HAUPTAUFGABE_2_DELAUNAYPRIORITYQUEUE_H diff --git a/delaunay/DelaunaySet.cpp b/delaunay/DelaunaySet.cpp new file mode 100644 index 0000000..413cc17 --- /dev/null +++ b/delaunay/DelaunaySet.cpp @@ -0,0 +1,29 @@ +// +// Created by jgier on 27.06.2022. +// + +#include +#include "DelaunaySet.h" + + +void DelaunaySet::insert(const DelaunaySet::Terminal &t) { + _less_set.emplace(t); + _greater_set.emplace(t); +} + +void DelaunaySet::del(const DelaunaySet::Terminal &t) { + _less_set.erase(t); + _greater_set.erase(t); +} + +std::optional DelaunaySet::predecessor(const DelaunaySet::Terminal &t) const { + auto ret = *_greater_set.upper_bound(t); + //assert(greater_fun(t, ret)); + return greater_fun(t, ret) ? std::make_optional(ret) : std::nullopt; +} + +std::optional DelaunaySet::sucessor(const DelaunaySet::Terminal &t) const { + auto ret = *_less_set.upper_bound(t); + //assert(less_fun(t, ret)); + return less_fun(t, ret) ? std::make_optional(ret) : std::nullopt; +} diff --git a/delaunay/DelaunaySet.h b/delaunay/DelaunaySet.h new file mode 100644 index 0000000..49577fa --- /dev/null +++ b/delaunay/DelaunaySet.h @@ -0,0 +1,33 @@ +// +// Created by jgier on 27.06.2022. +// + +#ifndef HAUPTAUFGABE_2_DELAUNAYSET_H +#define HAUPTAUFGABE_2_DELAUNAYSET_H + +#include "DelaunayGraph.h" +#include + +class DelaunaySet { +public: + using Terminal = DelaunayGraph::Terminal; + + void insert(Terminal const &t); + void del(Terminal const &t); + [[nodiscard]] std::optional sucessor(Terminal const &t) const; + [[nodiscard]] std::optional predecessor(Terminal const &t) const; + +private: + static constexpr auto less_fun = [](Terminal const &lhs, Terminal const &rhs) { + return (lhs.y_coord < rhs.y_coord) or (rhs.y_coord == lhs.y_coord and rhs.x_coord < lhs.x_coord); + }; + static constexpr auto greater_fun = [](Terminal const &lhs, Terminal const &rhs) { + return (lhs.y_coord > rhs.y_coord) or (rhs.y_coord == lhs.y_coord and rhs.x_coord > lhs.x_coord); + }; + + std::set _less_set; + std::set _greater_set; +}; + + +#endif //HAUPTAUFGABE_2_DELAUNAYSET_H diff --git a/dijkstra/DijkstraGraph.cpp b/dijkstra/DijkstraGraph.cpp index 26ce7a4..546f665 100644 --- a/dijkstra/DijkstraGraph.cpp +++ b/dijkstra/DijkstraGraph.cpp @@ -5,8 +5,8 @@ #include #include #include "DijkstraGraph.h" -#include "FibonacciHeap.h" -#include "StandardHeap.h" +#include "../heap/FibonacciHeap.h" +#include "../heap/StandardHeap.h" #include #include diff --git a/googletest b/googletest new file mode 160000 index 0000000..9406a60 --- /dev/null +++ b/googletest @@ -0,0 +1 @@ +Subproject commit 9406a60c7839052e4944ea4dbc8344762a89f9bd diff --git a/dijkstra/BinaryHeap.cpp b/heap/BinaryHeap.cpp similarity index 100% rename from dijkstra/BinaryHeap.cpp rename to heap/BinaryHeap.cpp diff --git a/dijkstra/BinaryHeap.h b/heap/BinaryHeap.h similarity index 100% rename from dijkstra/BinaryHeap.h rename to heap/BinaryHeap.h diff --git a/dijkstra/FibonacciHeap.cpp b/heap/FibonacciHeap.cpp similarity index 100% rename from dijkstra/FibonacciHeap.cpp rename to heap/FibonacciHeap.cpp diff --git a/dijkstra/FibonacciHeap.h b/heap/FibonacciHeap.h similarity index 100% rename from dijkstra/FibonacciHeap.h rename to heap/FibonacciHeap.h diff --git a/dijkstra/Heap.h b/heap/Heap.h similarity index 100% rename from dijkstra/Heap.h rename to heap/Heap.h diff --git a/dijkstra/StandardHeap.cpp b/heap/StandardHeap.cpp similarity index 100% rename from dijkstra/StandardHeap.cpp rename to heap/StandardHeap.cpp diff --git a/dijkstra/StandardHeap.h b/heap/StandardHeap.h similarity index 100% rename from dijkstra/StandardHeap.h rename to heap/StandardHeap.h diff --git a/main.cpp b/main.cpp index 1844943..fbf58e4 100644 --- a/main.cpp +++ b/main.cpp @@ -1,31 +1,49 @@ #include +#include #include "STPFileParser.h" -#include "DelaunayGraphFactory.h" +#include "MehlhornDelaunayGraphFactory.h" #include "kruskal/kruskal_ind.h" #include "graph/RectilinearGraph.h" int main(int argc, char *argv[]) { - STPFileParser parser("rect_instance_20.stp"); - std::cout << std::endl << "--------Start instance-----------" << std::endl; - Graph graph = parser.create_graph(); - //graph.print_graph(std::cout); + STPFileParser parser("rect_instance_19.stp"); +// std::cout << std::endl << "--------Start instance-----------" << std::endl; +// Graph graph = parser.create_graph(); +// //graph.print_graph(std::cout); +// std::cout << std::endl << "--------Delaunay result----------" << std::endl; +// Graph delaunay_graph = MehlhornDelaunayGraphFactory::create_delaunay_graph(graph); +// //delaunay_graph.print_graph(std::cout); +// std::cout << std::endl << "---------Kruskal result----------" << std::endl; +// Graph kruskal_graph = kruskal(delaunay_graph); +// //kruskal_graph.print_graph((std::cout)); +// std::cout << std::endl << "-------Rectilinear result--------" << std::endl; +// RectilinearGraph rectilinear_graph(kruskal_graph); +// auto graph_2 = rectilinear_graph.create_graph(); +// //graph_2.print_graph(std::cout); +// std::cout << std::endl << "-------2nd Kruskal result--------" << std::endl; +// auto kruskal_graph_2 = kruskal(graph_2); +// kruskal_graph_2.print_graph(std::cout); +// +// std::cout << std::endl << "----------PostScript-------------" << std::endl; +// RectilinearGraph output_graph(kruskal_graph_2); +// output_graph.print_as_postscript(std::cout, "../PostScript/template.ps"); + std::cout << std::endl << "--------Delaunay result----------" << std::endl; - Graph delaunay_graph = DelaunayGraphFactory::create_delaunay_graph(graph); - //delaunay_graph.print_graph(std::cout); + DelaunayGraph delaunay_graph = parser.create_delaunay_graph(); + delaunay_graph.translate_from_1_to_infty_norm(); + delaunay_graph.calculate(); + delaunay_graph.translate_from_infty_to_1_norm(); + delaunay_graph.print_as_postscript(std::cout, "../PostScript/template.ps"); + delaunay_graph.primitive_print(std::cout); + auto graph = delaunay_graph.export_graph(); + graph.print_graph(std::cout); std::cout << std::endl << "---------Kruskal result----------" << std::endl; - Graph kruskal_graph = kruskal(delaunay_graph); - //kruskal_graph.print_graph((std::cout)); - std::cout << std::endl << "-------Rectilinear result--------" << std::endl; - RectilinearGraph rectilinear_graph(kruskal_graph); - auto graph_2 = rectilinear_graph.create_graph(); - //graph_2.print_graph(std::cout); - std::cout << std::endl << "-------2nd Kruskal result--------" << std::endl; - auto kruskal_graph_2 = kruskal(graph_2); - kruskal_graph_2.print_graph(std::cout); - - std::cout << std::endl << "----------PostScript-------------" << std::endl; - RectilinearGraph output_graph(kruskal_graph_2); - output_graph.print_as_postscript(std::cout, "../PostScript/template.ps"); + auto kruskal_graph = kruskal(graph); + kruskal_graph.print_graph(std::cout); + RectilinearGraph output_graph(kruskal_graph); + std::ofstream file("../PostScript/test.ps"); + output_graph.print_as_postscript(file, "../PostScript/template.ps"); + file.close(); return 0; diff --git a/typedefs.h b/typedefs.h index 93a14ee..a50ee87 100644 --- a/typedefs.h +++ b/typedefs.h @@ -9,6 +9,7 @@ using NodeId = int; using EdgeId = int; +using GridUnit = int; using WeightType = int; From bfdc026245b2f814e00e4da7fb4ddbae39025c62 Mon Sep 17 00:00:00 2001 From: JakobGie <44549596+armytrong@users.noreply.github.com> Date: Wed, 29 Jun 2022 00:34:04 +0200 Subject: [PATCH 11/25] Update CMakeLists.txt remove googletest from CMakeLists.txt --- CMakeLists.txt | 3 --- 1 file changed, 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7b2f462..105eea6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,6 +23,3 @@ add_executable(Hauptaufgabe_2 graph/RectilinearGraph.cpp graph/RectilinearGraph.h heap/BinaryHeap.cpp heap/BinaryHeap.h heap/Heap.h googletest/MainTest.cpp delaunay/DelaunayGraph.cpp delaunay/DelaunayGraph.h delaunay/DelaunaySet.cpp delaunay/DelaunaySet.h delaunay/DelaunayPriorityQueue.cpp delaunay/DelaunayPriorityQueue.h) - - -add_subdirectory(googletest) From d71f2ff176e4699b8a1d8612635d5bc48209f83b Mon Sep 17 00:00:00 2001 From: JakobGie <44549596+armytrong@users.noreply.github.com> Date: Wed, 29 Jun 2022 00:36:13 +0200 Subject: [PATCH 12/25] Repair CMakeLists.txt again... --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 105eea6..dc32f49 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,4 +22,4 @@ add_executable(Hauptaufgabe_2 heap/StandardHeap.h graph/RectilinearGraph.cpp graph/RectilinearGraph.h - heap/BinaryHeap.cpp heap/BinaryHeap.h heap/Heap.h googletest/MainTest.cpp delaunay/DelaunayGraph.cpp delaunay/DelaunayGraph.h delaunay/DelaunaySet.cpp delaunay/DelaunaySet.h delaunay/DelaunayPriorityQueue.cpp delaunay/DelaunayPriorityQueue.h) + heap/BinaryHeap.cpp heap/BinaryHeap.h heap/Heap.h delaunay/DelaunayGraph.cpp delaunay/DelaunayGraph.h delaunay/DelaunaySet.cpp delaunay/DelaunaySet.h delaunay/DelaunayPriorityQueue.cpp delaunay/DelaunayPriorityQueue.h) From 93878528ca9440eb3bf38e6da219a92a55836650 Mon Sep 17 00:00:00 2001 From: Jakob Gierschmann Date: Wed, 29 Jun 2022 01:04:26 +0200 Subject: [PATCH 13/25] refactorings --- CMakeLists.txt | 2 ++ delaunay/DelaunayGraph.cpp | 18 +++++++++--------- delaunay/DelaunaySet.cpp | 2 +- delaunay/DelaunaySet.h | 2 +- graph/Graph.h | 2 +- graph/RectilinearGraph.h | 2 +- heap/BinaryHeap.cpp | 4 +--- kruskal/kruskal_ind.cpp | 1 + kruskal/kruskal_ind.h | 7 +++---- 9 files changed, 20 insertions(+), 20 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index dc32f49..86374db 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,6 +3,8 @@ project(Hauptaufgabe_2) set(CMAKE_CXX_STANDARD 20) +include_directories(.) + add_executable(Hauptaufgabe_2 main.cpp SteinerTreeApproximator.cpp diff --git a/delaunay/DelaunayGraph.cpp b/delaunay/DelaunayGraph.cpp index 2b0aa9c..a185493 100644 --- a/delaunay/DelaunayGraph.cpp +++ b/delaunay/DelaunayGraph.cpp @@ -36,12 +36,12 @@ void DelaunayGraph::calculate() { age_counter++; if (P.status == ACTIVE) { Y.insert(P.terminal); - auto sucessor = Y.sucessor(P.terminal); + auto successor = Y.successor(P.terminal); auto predecessor = Y.predecessor((P.terminal)); - assert(sucessor.has_value() and predecessor.has_value()); - if (sucessor->x_coord != -_max_x - 1) { - _edges.push_back({P.terminal, *sucessor}); - update_inactivation_records(X, Y, ages[P.terminal] >= ages[*sucessor] ? *sucessor : P.terminal); + assert(successor.has_value() and predecessor.has_value()); + if (successor->x_coord != -_max_x - 1) { + _edges.push_back({P.terminal, *successor}); + update_inactivation_records(X, Y, ages[P.terminal] >= ages[*successor] ? *successor : P.terminal); } if (predecessor->x_coord != -_max_x - 1) { _edges.push_back({P.terminal, *predecessor}); @@ -50,9 +50,9 @@ void DelaunayGraph::calculate() { } else { auto q = Y.predecessor(P.terminal); Y.del(P.terminal); - auto sucessor = Y.sucessor(*q); - if (q->x_coord != -_max_x - 1 and sucessor->x_coord != -_max_x - 1) _edges.push_back({*q, *sucessor}); - update_inactivation_records(X, Y, ages[*q] >= ages[*sucessor] ? *sucessor : *q); + auto successor = Y.successor(*q); + if (q->x_coord != -_max_x - 1 and successor->x_coord != -_max_x - 1) _edges.push_back({*q, *successor}); + update_inactivation_records(X, Y, ages[*q] >= ages[*successor] ? *successor : *q); } } } @@ -60,7 +60,7 @@ void DelaunayGraph::calculate() { void DelaunayGraph::update_inactivation_records(DelaunayPriorityQueue &X, const DelaunaySet &Y, DelaunayGraph::Terminal terminal) { auto r = Y.predecessor(terminal); - auto q = Y.sucessor(terminal); + auto q = Y.successor(terminal); if (r->x_coord > terminal.x_coord and q->x_coord >= terminal.x_coord) { if (X.find_inactivation_record(terminal).has_value()) { X.change_priority(terminal, terminal.x_coord + q->y_coord - r->y_coord); diff --git a/delaunay/DelaunaySet.cpp b/delaunay/DelaunaySet.cpp index 413cc17..d684f0f 100644 --- a/delaunay/DelaunaySet.cpp +++ b/delaunay/DelaunaySet.cpp @@ -22,7 +22,7 @@ std::optional DelaunaySet::predecessor(const DelaunaySet: return greater_fun(t, ret) ? std::make_optional(ret) : std::nullopt; } -std::optional DelaunaySet::sucessor(const DelaunaySet::Terminal &t) const { +std::optional DelaunaySet::successor(const DelaunaySet::Terminal &t) const { auto ret = *_less_set.upper_bound(t); //assert(less_fun(t, ret)); return less_fun(t, ret) ? std::make_optional(ret) : std::nullopt; diff --git a/delaunay/DelaunaySet.h b/delaunay/DelaunaySet.h index 49577fa..766167f 100644 --- a/delaunay/DelaunaySet.h +++ b/delaunay/DelaunaySet.h @@ -14,7 +14,7 @@ class DelaunaySet { void insert(Terminal const &t); void del(Terminal const &t); - [[nodiscard]] std::optional sucessor(Terminal const &t) const; + [[nodiscard]] std::optional successor(Terminal const &t) const; [[nodiscard]] std::optional predecessor(Terminal const &t) const; private: diff --git a/graph/Graph.h b/graph/Graph.h index d4426a9..577229d 100644 --- a/graph/Graph.h +++ b/graph/Graph.h @@ -7,7 +7,7 @@ #include #include -#include "../typedefs.h" +#include "typedefs.h" class Graph { public: diff --git a/graph/RectilinearGraph.h b/graph/RectilinearGraph.h index 5e1deea..b97da36 100644 --- a/graph/RectilinearGraph.h +++ b/graph/RectilinearGraph.h @@ -9,7 +9,7 @@ #include #include #include -#include "../typedefs.h" +#include "typedefs.h" #include "Graph.h" class RectilinearGraph { diff --git a/heap/BinaryHeap.cpp b/heap/BinaryHeap.cpp index 257c797..a014349 100644 --- a/heap/BinaryHeap.cpp +++ b/heap/BinaryHeap.cpp @@ -20,6 +20,4 @@ bool BinaryHeap::empty() const { return false; } -BinaryHeap::~BinaryHeap() { - -} +BinaryHeap::~BinaryHeap() = default; diff --git a/kruskal/kruskal_ind.cpp b/kruskal/kruskal_ind.cpp index fe1de79..1478e28 100644 --- a/kruskal/kruskal_ind.cpp +++ b/kruskal/kruskal_ind.cpp @@ -2,6 +2,7 @@ #include #include #include "disjoint_set_2.h" +#include "graph/Graph.h" Graph kruskal(const Graph &input_graph) { Graph return_graph(input_graph.num_nodes(), input_graph.terminals()); diff --git a/kruskal/kruskal_ind.h b/kruskal/kruskal_ind.h index f1c712c..2e39443 100644 --- a/kruskal/kruskal_ind.h +++ b/kruskal/kruskal_ind.h @@ -1,13 +1,12 @@ #pragma once -#include -#include "disjoint_set_2.h" -#include "../graph/Graph.h" - //FIXED das Resultat returnen, nicht output parameter //FIXED variablen ausschreiben //FIXED definition in das c-file und wenn nicht, muss die Funktion inline sein //Als kruskal routine ok, aber es wäre sinnvoll noch eine Instanz-klasse oder so bereitzustellen und dann die als input zu nehmen in einer //approximate_steiner_tree funktion oder so + +class Graph; + Graph kruskal(const Graph &input_graph); From cc1c4f13a4e057ae299cacea3bbe7254d1baf163 Mon Sep 17 00:00:00 2001 From: Jakob Gierschmann Date: Wed, 29 Jun 2022 21:26:06 +0200 Subject: [PATCH 14/25] created ArgumentHandler and ported kruskal to DelaunayGraph --- ArgumentHandler.cpp | 45 +++++++++++++++++ ArgumentHandler.h | 46 +++++++++++++++++ CMakeLists.txt | 2 +- STPFileParser.cpp | 84 ++++++++++++++++++-------------- STPFileParser.h | 3 +- delaunay/DelaunayGraph.cpp | 50 +++++++++++++++++-- delaunay/DelaunayGraph.h | 26 +++++++--- delaunay/DelaunayPriorityQueue.h | 6 +-- dijkstra/DijkstraGraph.h | 5 +- graph/RectilinearGraph.cpp | 2 +- graph/RectilinearGraph.h | 2 +- kruskal/kruskal_ind.cpp | 33 +++++++++++++ kruskal/kruskal_ind.h | 3 ++ main.cpp | 72 ++++++++++++++------------- typedefs.h | 1 - 15 files changed, 286 insertions(+), 94 deletions(-) create mode 100644 ArgumentHandler.cpp create mode 100644 ArgumentHandler.h diff --git a/ArgumentHandler.cpp b/ArgumentHandler.cpp new file mode 100644 index 0000000..b3dacb8 --- /dev/null +++ b/ArgumentHandler.cpp @@ -0,0 +1,45 @@ +// +// Created by jgier on 29.06.2022. +// + +#include "ArgumentHandler.h" +#include + +const ArgumentHandler::Parms &ArgumentHandler::parms() { + return _parms; +} + +ArgumentHandler::ArgumentHandler(int argc, char **argv) : _parms{"", "", OutputFormat::STP, 0, false, true} { + for (int i = 1; i < argc; i++) { + std::string arg(argv[i]); + if (arg == "-ps") { + _parms.output_format = OutputFormat::PS; + } else if (arg == "-stp") { + _parms.output_format = OutputFormat::STP; + } else if (arg == "-o") { + i++; + if (i < argc) { + _parms.output_filename = argv[i]; + } + } else if (arg == "-v" or arg == "--verbose") { + _parms.verbose = true; + } else if (arg == "-mi" or arg == "--manual_input") { + std::cout << "Specify input file: "; + std::cin >> _parms.input_filename; + } else if (arg == "-mo" or arg == "--manual_output") { + std::cout << "Specify output file: "; + std::cin >> _parms.output_filename; + } else if (arg == "-h" or arg == "--help") { + print_usage(argv[0]); + _parms.run = false; + } else if (_parms.input_filename.empty()) { + _parms.input_filename = arg; + } + } +} + +void ArgumentHandler::print_usage(std::string const &program_name) { + std::cerr << "Usage: " << program_name + << " [-ps] [-stp] [-o OUTPUT_FILE] [-mi] [-mo] [-v] [INPUT_FILENAME]" + << std::endl; +} diff --git a/ArgumentHandler.h b/ArgumentHandler.h new file mode 100644 index 0000000..543b770 --- /dev/null +++ b/ArgumentHandler.h @@ -0,0 +1,46 @@ +// +// Created by jgier on 29.06.2022. +// + +#ifndef HAUPTAUFGABE_2_ARGUMENTHANDLER_H +#define HAUPTAUFGABE_2_ARGUMENTHANDLER_H + + +#include +#include +#include +#include + +enum struct OutputFormat { + PS, STP +}; + +class ArgumentHandler { +public: + struct OstreamWrapper { + explicit OstreamWrapper(std::ostream &o) : ostream(o) {} + + std::ostream &ostream; + }; + + struct Parms { + std::string input_filename; + std::string output_filename; + OutputFormat output_format; + size_t instances_to_skip; + bool verbose; + bool run; + }; + + ArgumentHandler(int argc, char **argv); + + static void print_usage(std::string const &program_name); + + Parms const &parms(); +private: + Parms _parms; + std::optional _file; +}; + + +#endif //HAUPTAUFGABE_2_ARGUMENTHANDLER_H diff --git a/CMakeLists.txt b/CMakeLists.txt index 86374db..038518e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,4 +24,4 @@ add_executable(Hauptaufgabe_2 heap/StandardHeap.h graph/RectilinearGraph.cpp graph/RectilinearGraph.h - heap/BinaryHeap.cpp heap/BinaryHeap.h heap/Heap.h delaunay/DelaunayGraph.cpp delaunay/DelaunayGraph.h delaunay/DelaunaySet.cpp delaunay/DelaunaySet.h delaunay/DelaunayPriorityQueue.cpp delaunay/DelaunayPriorityQueue.h) + heap/BinaryHeap.cpp heap/BinaryHeap.h heap/Heap.h delaunay/DelaunayGraph.cpp delaunay/DelaunayGraph.h delaunay/DelaunaySet.cpp delaunay/DelaunaySet.h delaunay/DelaunayPriorityQueue.cpp delaunay/DelaunayPriorityQueue.h ArgumentHandler.cpp ArgumentHandler.h) diff --git a/STPFileParser.cpp b/STPFileParser.cpp index 24d16da..2306045 100644 --- a/STPFileParser.cpp +++ b/STPFileParser.cpp @@ -7,61 +7,76 @@ #include #include #include +#include #include "STPFileParser.h" constexpr char FILE_HEADER[] = "33D32945 STP File, STP Format Version 1.0"; -STPFileParser::STPFileParser(std::string filename) : _filename(std::move(filename)) { - std::ifstream file = open_input_file(); +STPFileParser::STPFileParser(std::string filename, size_t instances_to_skip) : _filename(std::move(filename)) { _num_nodes = -1; - std::string line; - std::getline(file, line); // read the first line of the file - while (line.empty()) // find the first non-empty line - { - std::getline(file, line); + std::ifstream file(_filename); + if (not file and not _filename.empty()) { + throw std::invalid_argument("input file not found"); } - // check for valid file header - remove_carriage_return(line); - if (line != FILE_HEADER) { - throw std::invalid_argument("Invalid file format."); + std::istream &input = _filename.empty() ? std::cin : file; + + // skip the required number of instances in the input + std::string line; + safe_getline(input, line); // read the first line of the input +// while (line.empty()) // find the first non-empty line +// { +// safe_getline(input, line); +// } + for (size_t i = 0; i < instances_to_skip + 1; i++) { + while (line != FILE_HEADER and not input.eof()) { + safe_getline(input, line); + remove_carriage_return(line); + } } + // check for valid input header +// if (line != FILE_HEADER) { +// throw std::invalid_argument("Invalid input format."); +// } + bool graph_found = false; bool terminals_found = false; bool coordinates_found = false; while (line != "EOF") { - std::getline(file, line); + if (not safe_getline(input, line)) { + throw std::invalid_argument("Invalid input format: EOF expected"); + } remove_carriage_return(line); if (line == "SECTION Graph") { if (graph_found) { - throw std::invalid_argument("Invalid file format: Multiple graphs in file."); + throw std::invalid_argument("Invalid input format: Multiple graphs in input."); } else { - read_graph_from_file(file); + read_graph_from_file(input); graph_found = true; } } else if (line == "SECTION Terminals") { if (terminals_found) { - throw std::invalid_argument("Invalid file format: Multiple terminal sections in file."); + throw std::invalid_argument("Invalid input format: Multiple terminal sections in input."); } else { - read_terminals_from_file(file); + read_terminals_from_file(input); terminals_found = true; } } else if (line == "SECTION Coordinates") { if (coordinates_found) { - throw std::invalid_argument("Invalid file format: Multiple coordinate sections in file."); + throw std::invalid_argument("Invalid input format: Multiple coordinate sections in input."); } _num_terminals = _num_nodes; _num_nodes = 10000 * 10000; - read_coordinates_from_file(file); + read_coordinates_from_file(input); coordinates_found = true; } } if (!graph_found) { - throw std::invalid_argument("Invalid file format: No graph in file."); + throw std::invalid_argument("Invalid input format: No graph in input."); } } @@ -77,10 +92,9 @@ void STPFileParser::read_graph_from_file(std::istream &file) { int num_edges = 0; while (line != "END") { - if (!std::getline(file, line) or line == "EOF" or line == "EOF\r") { + if (!safe_getline(file, line) or line == "EOF" or line == "EOF\r") { throw std::invalid_argument("Invalid file format."); } - remove_carriage_return(line); std::stringstream ss(line); ss >> specifier >> val_1 >> val_2 >> val_3; if (specifier == "Nodes") { @@ -109,10 +123,9 @@ void STPFileParser::read_terminals_from_file(std::istream &file) { int val; while (line != "END") { - if (!std::getline(file, line) or line == "EOF" or line == "EOF\r") { + if (!safe_getline(file, line) or line == "EOF" or line == "EOF\r") { throw std::invalid_argument("Invalid file format."); } - remove_carriage_return(line); std::stringstream ss(line); ss >> specifier >> val; if (specifier == "Terminals") { @@ -133,7 +146,7 @@ void STPFileParser::read_coordinates_from_file(std::istream &file) { std::string specifier; int terminal_id, x_coord, y_coord; while (line != "END") { - if (!std::getline(file, line) or line == "EOF" or line == "EOF\r") { + if (!safe_getline(file, line) or line == "EOF" or line == "EOF\r") { throw std::invalid_argument("Invalid file format."); } remove_carriage_return(line); @@ -149,17 +162,7 @@ void STPFileParser::read_coordinates_from_file(std::istream &file) { } } -std::ifstream STPFileParser::open_input_file() const { - std::ifstream file{_filename, std::ifstream::in}; - // open file - if (!file) { - throw std::invalid_argument("Cannot open file " + _filename + "."); - } - //check_file_header(file); - return file; -} - -void STPFileParser::remove_carriage_return(std::string &s) { +inline void STPFileParser::remove_carriage_return(std::string &s) { while (s[s.size() - 1] == '\r') { s = s.erase(s.size() - 1); } @@ -170,9 +173,18 @@ DelaunayGraph STPFileParser::create_delaunay_graph() { auto sqrt = static_cast(std::sqrt(_num_nodes)); assert(sqrt * sqrt == _num_nodes); for (auto terminal_id: _terminals) { - delaunay_graph.add_terminal(terminal_id % sqrt, static_cast(terminal_id / sqrt), terminal_id); + delaunay_graph.add_terminal(terminal_id % sqrt, static_cast(terminal_id / sqrt)); } return delaunay_graph; } +inline std::istream &STPFileParser::safe_getline(std::istream &istream, std::string &string) { + auto &ret = std::getline(istream, string); + if (not ret or istream.eof()) { + throw std::invalid_argument("invalid input format expected EOF"); + } + remove_carriage_return(string); + return ret; +} + diff --git a/STPFileParser.h b/STPFileParser.h index 570569c..270d17e 100644 --- a/STPFileParser.h +++ b/STPFileParser.h @@ -12,7 +12,7 @@ class STPFileParser { public: - explicit STPFileParser(std::string filename); + explicit STPFileParser(std::string filename, size_t instances_to_skip = 0); Graph create_graph(); DelaunayGraph create_delaunay_graph(); @@ -29,6 +29,7 @@ class STPFileParser { static void remove_carriage_return(std::string &s); + static std::istream &safe_getline(std::istream &istream, std::string &string); std::string _filename; NodeId _num_nodes; NodeId _num_terminals; diff --git a/delaunay/DelaunayGraph.cpp b/delaunay/DelaunayGraph.cpp index a185493..ad17132 100644 --- a/delaunay/DelaunayGraph.cpp +++ b/delaunay/DelaunayGraph.cpp @@ -11,10 +11,12 @@ #include #include -void DelaunayGraph::add_terminal(GridUnit x_coord, GridUnit y_coord, NodeId id) { - _terminals.emplace_back(x_coord, y_coord, id); +void DelaunayGraph::add_terminal(GridUnit x_coord, GridUnit y_coord) { + _terminals.emplace_back(x_coord, y_coord, _terminals.size()); _max_x = std::max(_max_x, std::abs(x_coord)); _max_y = std::max(_max_y, std::abs(y_coord)); + _orig_max_x = _max_x; + _orig_max_y = _max_y; } @@ -83,7 +85,7 @@ void DelaunayGraph::primitive_print(std::ostream &os) { } Graph DelaunayGraph::export_graph() { - NodeId sqrt = std::max(_max_x, _max_y) + 1; + NodeId sqrt = std::max(_orig_max_x, _orig_max_y) + 1; NodeId num_nodes = sqrt * sqrt; std::vector terminals; for (auto const &terminal: _terminals) { @@ -119,6 +121,8 @@ void DelaunayGraph::translate_terminal_from_infty_to_1_norm(DelaunayGraph::Termi void DelaunayGraph::translate_from_1_to_infty_norm() { for (auto &terminal: _terminals) { translate_terminal_from_1_to_infty_norm(terminal); + _max_x = std::max(std::abs(terminal.x_coord), _max_x); + _max_y = std::max(std::abs(terminal.y_coord), _max_y); } for (auto &edge: _edges) { translate_terminal_from_1_to_infty_norm(edge.terminal_a); @@ -144,7 +148,7 @@ void DelaunayGraph::print_as_postscript(std::ostream &os, const std::string &bas os << base_file.rdbuf(); base_file.close(); - GridUnit grid_width = static_cast(std::sqrt(std::max(_max_x, _max_y))); + auto grid_width = static_cast(std::sqrt(std::max(_orig_max_x, _orig_max_y))); os << std::endl; os << "%%BeginSetup" << std::endl << std::endl; @@ -175,3 +179,41 @@ void DelaunayGraph::print_as_postscript(std::ostream &os, const std::string &bas //std::endl; os << "EndPlot" << std::endl; } + +const std::vector &DelaunayGraph::terminals() const { return _terminals; } + +EdgeId DelaunayGraph::num_edges() const { return static_cast(_edges.size()); } + +const std::vector &DelaunayGraph::edges() const { return _edges; } + +NodeId DelaunayGraph::num_terminals() const { return _terminals.size(); } + +void DelaunayGraph::add_edge(DelaunayGraph::Terminal terminal_a, DelaunayGraph::Terminal terminal_b) { + _edges.push_back({terminal_a, terminal_b}); +} + +DelaunayGraph::DelaunayGraph(std::vector terminals) : + _max_x(0), _max_y(0), _orig_max_x(0), _orig_max_y(0), _terminals(std::move(terminals)) { + for (auto const &terminal: _terminals) { + _max_x = std::max(std::abs(terminal.x_coord), _max_x); + _max_y = std::max(std::abs(terminal.y_coord), _max_y); + _orig_max_x = _max_x; + _orig_max_y = _max_y; + } +} + +bool DelaunayGraph::Terminal::operator==(const DelaunayGraph::Terminal &other) const { + return x_coord == other.x_coord and y_coord == other.y_coord; +} + +bool DelaunayGraph::Terminal::operator<(const DelaunayGraph::Terminal &other) const { + return x_coord < other.x_coord or (x_coord == other.x_coord and y_coord < other.y_coord); +} + +GridUnit DelaunayGraph::Terminal::distance(const DelaunayGraph::Terminal &other) const { + return std::abs(x_coord - other.x_coord) + std::abs(y_coord - other.y_coord); +} + +bool DelaunayGraph::Edge::operator<(const DelaunayGraph::Edge &other) const { + return terminal_a.distance(terminal_b) < other.terminal_a.distance((other.terminal_b)); +} diff --git a/delaunay/DelaunayGraph.h b/delaunay/DelaunayGraph.h index 87fb17a..77e08d6 100644 --- a/delaunay/DelaunayGraph.h +++ b/delaunay/DelaunayGraph.h @@ -7,6 +7,7 @@ #include +#include #include "../typedefs.h" #include "../graph/Graph.h" @@ -18,7 +19,7 @@ class DelaunaySet; class DelaunayGraph { public: - DelaunayGraph() : _max_x(0), _max_y(0) {} + DelaunayGraph() : _max_x(0), _max_y(0), _orig_max_x(0), _orig_max_y(0) {} struct Terminal { Terminal(GridUnit x_coord, GridUnit y_coord, NodeId id = -1) : x_coord(x_coord), y_coord(y_coord), id(id) {} @@ -27,30 +28,39 @@ class DelaunayGraph { GridUnit y_coord; NodeId id; - bool operator==(Terminal const &other) const { return x_coord == other.x_coord and y_coord == other.y_coord; } + bool operator==(Terminal const &other) const; - bool operator<(Terminal const &other) const { - return x_coord < other.x_coord or (x_coord == other.x_coord and y_coord < other.y_coord); - } + bool operator<(Terminal const &other) const; + + [[nodiscard]] GridUnit distance(Terminal const &other) const; }; + explicit DelaunayGraph(std::vector terminals); + struct Edge { Terminal terminal_a; Terminal terminal_b; + [[nodiscard]] bool operator<(Edge const &other) const; }; - void add_terminal(GridUnit x_coord, GridUnit y_coord, NodeId id = -1); + void add_terminal(GridUnit x_coord, GridUnit y_coord); + void add_edge(Terminal terminal_a, Terminal terminal_b); void calculate(); void primitive_print(std::ostream &os); void translate_from_1_to_infty_norm(); void translate_from_infty_to_1_norm(); - Graph export_graph(); void print_as_postscript(std::ostream &os, const std::string &base_file_name); + [[nodiscard]] NodeId num_terminals() const; + [[nodiscard]] EdgeId num_edges() const; + [[nodiscard]] std::vector const &terminals() const; + [[nodiscard]] std::vector const &edges() const; + + private: static void update_inactivation_records(DelaunayPriorityQueue &X, DelaunaySet const &Y, Terminal terminal); @@ -59,7 +69,7 @@ class DelaunayGraph { std::vector _terminals; std::vector _edges; - GridUnit _max_x, _max_y; + GridUnit _max_x, _max_y, _orig_max_x, _orig_max_y; }; diff --git a/delaunay/DelaunayPriorityQueue.h b/delaunay/DelaunayPriorityQueue.h index d693c05..fa3b2e6 100644 --- a/delaunay/DelaunayPriorityQueue.h +++ b/delaunay/DelaunayPriorityQueue.h @@ -17,7 +17,7 @@ enum ActiveInactive { class DelaunayPriorityQueue { public: DelaunayPriorityQueue(GridUnit max_x, GridUnit max_y) : MINIMUM_RECORD( - {{-max_x, -max_y}, -max_x - max_y, INACTIVE}) {} + {{-max_x - 1, -max_y - 1}, -max_x - max_y - 1, INACTIVE}) {} using Terminal = DelaunayGraph::Terminal; struct Record { @@ -28,9 +28,9 @@ class DelaunayPriorityQueue { Record extract_min(); void insert(Terminal const &t, GridUnit trans, ActiveInactive status); - std::optional find_inactivation_record(Terminal t) const; void change_priority(Terminal const &t, GridUnit new_trans); - bool empty() const; + [[nodiscard]] std::optional find_inactivation_record(Terminal t) const; + [[nodiscard]] bool empty() const; private: static constexpr auto greater_fun = [](Record const &rhs, Record const &lhs) { diff --git a/dijkstra/DijkstraGraph.h b/dijkstra/DijkstraGraph.h index dccf9de..84fbf5f 100644 --- a/dijkstra/DijkstraGraph.h +++ b/dijkstra/DijkstraGraph.h @@ -7,8 +7,8 @@ #include #include -#include "../typedefs.h" -#include "../graph/Graph.h" +#include "typedefs.h" +#include "graph/Graph.h" class DijkstraGraph { public: @@ -23,7 +23,6 @@ class DijkstraGraph { bool included = false; }; - // TODO: make DijkstraGraph mutable explicit DijkstraGraph(Graph const &graph); diff --git a/graph/RectilinearGraph.cpp b/graph/RectilinearGraph.cpp index d27f2ee..e6d1215 100644 --- a/graph/RectilinearGraph.cpp +++ b/graph/RectilinearGraph.cpp @@ -85,7 +85,7 @@ bool RectilinearGraph::validate_node(NodeId node) const { return node >= 0 and node < num_nodes(); } -Graph RectilinearGraph::create_graph() const { +Graph RectilinearGraph::export_graph() const { Graph result(num_nodes(), _terminals); for (NodeId i = 0; i < num_nodes(); i++) { if (_horizontal_edges_after_node[i]) { diff --git a/graph/RectilinearGraph.h b/graph/RectilinearGraph.h index b97da36..f48127f 100644 --- a/graph/RectilinearGraph.h +++ b/graph/RectilinearGraph.h @@ -35,7 +35,7 @@ class RectilinearGraph { return ret; } - [[nodiscard]] Graph create_graph() const; + [[nodiscard]] Graph export_graph() const; void print_as_postscript(std::ostream &os, const std::string &base_file_name); diff --git a/kruskal/kruskal_ind.cpp b/kruskal/kruskal_ind.cpp index 1478e28..22bca3e 100644 --- a/kruskal/kruskal_ind.cpp +++ b/kruskal/kruskal_ind.cpp @@ -3,6 +3,7 @@ #include #include "disjoint_set_2.h" #include "graph/Graph.h" +#include "delaunay/DelaunayGraph.h" Graph kruskal(const Graph &input_graph) { Graph return_graph(input_graph.num_nodes(), input_graph.terminals()); @@ -35,5 +36,37 @@ Graph kruskal(const Graph &input_graph) { } } + return return_graph; +} + +DelaunayGraph kruskal(DelaunayGraph const &delaunay_graph) { + DelaunayGraph return_graph(delaunay_graph.terminals()); + std::vector edge_ids(delaunay_graph.num_edges()); + std::iota(edge_ids.begin(), edge_ids.end(), 0); + + auto id_to_edge_projection = [&delaunay_graph](auto const edge_id) { return delaunay_graph.edges()[edge_id]; }; + + std::sort(edge_ids.begin(), edge_ids.end(), + [&id_to_edge_projection](EdgeId a, EdgeId b) { + return (id_to_edge_projection(a) < id_to_edge_projection(b)); + }); + // iteriere ueber die edges in der reihenfolge, wie sie im id-vector gespeichert sind + // diese syntax range | adaptor, bedeutet, dass die range (hier edge_ids) erst durch die adaptor-funktion gejagt wird + // (transform ruft die uebergebene funktion einmal mit jedem element auf und ist dann eine range ueber die return-values der funktion) + // es gibt z.b. auch std::views::filter(unary_predicate), was dann nur die elemente durchlaesst, fuer die das unaere praedikat true zurueckgibt + + Disjoint_Set set; + set.make_sets(return_graph.num_terminals()); + for (auto edge_id: edge_ids) { + auto const &edge = id_to_edge_projection(edge_id); + + if (!set.set_equals(edge.terminal_a.id, edge.terminal_b.id)) { + // unite the sets of the parents of the nodes of edge + set.unite(edge.terminal_a.id, edge.terminal_b.id); + // add edge to the vector of included edges + return_graph.add_edge(edge.terminal_a, edge.terminal_b); + } + } + return return_graph; } \ No newline at end of file diff --git a/kruskal/kruskal_ind.h b/kruskal/kruskal_ind.h index 2e39443..f0f2007 100644 --- a/kruskal/kruskal_ind.h +++ b/kruskal/kruskal_ind.h @@ -8,5 +8,8 @@ class Graph; +class DelaunayGraph; + Graph kruskal(const Graph &input_graph); +DelaunayGraph kruskal(const DelaunayGraph &delaunay_graph); diff --git a/main.cpp b/main.cpp index fbf58e4..d772999 100644 --- a/main.cpp +++ b/main.cpp @@ -1,48 +1,50 @@ #include -#include #include "STPFileParser.h" -#include "MehlhornDelaunayGraphFactory.h" #include "kruskal/kruskal_ind.h" #include "graph/RectilinearGraph.h" +#include "ArgumentHandler.h" int main(int argc, char *argv[]) { - STPFileParser parser("rect_instance_19.stp"); -// std::cout << std::endl << "--------Start instance-----------" << std::endl; -// Graph graph = parser.create_graph(); -// //graph.print_graph(std::cout); -// std::cout << std::endl << "--------Delaunay result----------" << std::endl; -// Graph delaunay_graph = MehlhornDelaunayGraphFactory::create_delaunay_graph(graph); -// //delaunay_graph.print_graph(std::cout); -// std::cout << std::endl << "---------Kruskal result----------" << std::endl; -// Graph kruskal_graph = kruskal(delaunay_graph); -// //kruskal_graph.print_graph((std::cout)); -// std::cout << std::endl << "-------Rectilinear result--------" << std::endl; -// RectilinearGraph rectilinear_graph(kruskal_graph); -// auto graph_2 = rectilinear_graph.create_graph(); -// //graph_2.print_graph(std::cout); -// std::cout << std::endl << "-------2nd Kruskal result--------" << std::endl; -// auto kruskal_graph_2 = kruskal(graph_2); -// kruskal_graph_2.print_graph(std::cout); -// -// std::cout << std::endl << "----------PostScript-------------" << std::endl; -// RectilinearGraph output_graph(kruskal_graph_2); -// output_graph.print_as_postscript(std::cout, "../PostScript/template.ps"); - - std::cout << std::endl << "--------Delaunay result----------" << std::endl; + ArgumentHandler argument_handler(argc, argv); + auto const &parms = argument_handler.parms(); + + if (not parms.run) return 0; + + auto print_verbose = [& parms](std::string const &s) { if (parms.verbose) std::cout << s << std::endl; }; + + STPFileParser parser(parms.input_filename, parms.instances_to_skip); + print_verbose("input parsed"); + DelaunayGraph delaunay_graph = parser.create_delaunay_graph(); delaunay_graph.translate_from_1_to_infty_norm(); delaunay_graph.calculate(); delaunay_graph.translate_from_infty_to_1_norm(); - delaunay_graph.print_as_postscript(std::cout, "../PostScript/template.ps"); - delaunay_graph.primitive_print(std::cout); - auto graph = delaunay_graph.export_graph(); - graph.print_graph(std::cout); - std::cout << std::endl << "---------Kruskal result----------" << std::endl; - auto kruskal_graph = kruskal(graph); - kruskal_graph.print_graph(std::cout); - RectilinearGraph output_graph(kruskal_graph); - std::ofstream file("../PostScript/test.ps"); - output_graph.print_as_postscript(file, "../PostScript/template.ps"); + print_verbose("Delaunay triangulation complete"); + + auto kruskal_graph = kruskal(delaunay_graph); + print_verbose("first kruskal complete"); + + RectilinearGraph rect_graph(kruskal_graph.export_graph()); + print_verbose("constructed rectiliear graph"); + + auto graph2 = rect_graph.export_graph(); + auto kruskal_graph2 = kruskal(graph2); + print_verbose("second kruskal complete"); + + std::cout << std::endl; + + std::ofstream file(parms.output_filename); + std::ostream &ostream = (parms.output_filename.empty() or not file) ? std::cout : file; + if (not file and not parms.output_filename.empty()) { + std::cerr << "unable to open output file, falling back to standard output" << std::endl; + } + + if (parms.output_format == OutputFormat::STP) { + kruskal_graph2.print_graph(ostream); + } else if (parms.output_format == OutputFormat::PS) { + RectilinearGraph output_graph(kruskal_graph2); + output_graph.print_as_postscript(ostream, "PostScript/template.ps"); + } file.close(); diff --git a/typedefs.h b/typedefs.h index a50ee87..ad83703 100644 --- a/typedefs.h +++ b/typedefs.h @@ -12,5 +12,4 @@ using EdgeId = int; using GridUnit = int; using WeightType = int; - #endif //HAUPTAUFGABE_2_TYPEDEFS_H From c8ac0d043696a6afc659353b6e486e29365e1241 Mon Sep 17 00:00:00 2001 From: Jakob Gierschmann Date: Wed, 29 Jun 2022 22:07:59 +0200 Subject: [PATCH 15/25] remove googletest --- .gitmodules | 3 --- googletest | 1 - 2 files changed, 4 deletions(-) delete mode 160000 googletest diff --git a/.gitmodules b/.gitmodules index 09d09ff..e69de29 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +0,0 @@ -[submodule "googletest"] - path = googletest - url = git@github.com:google/googletest.git diff --git a/googletest b/googletest deleted file mode 160000 index 9406a60..0000000 --- a/googletest +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 9406a60c7839052e4944ea4dbc8344762a89f9bd From db6bb688b5001df2bddca9c7c5704cdf9f1f8b63 Mon Sep 17 00:00:00 2001 From: JakobGie <44549596+armytrong@users.noreply.github.com> Date: Thu, 30 Jun 2022 12:53:42 +0200 Subject: [PATCH 16/25] code cleanup, regard static analysis --- .gitignore | 2 ++ ArgumentHandler.h | 6 ------ CMakeLists.txt | 2 -- MehlhornDelaunayGraphFactory.cpp | 7 ++++--- MehlhornDelaunayGraphFactory.h | 4 ++-- STPFileParser.cpp | 2 +- STPFileParser.h | 4 +--- SteinerTreeApproximator.cpp | 5 ----- SteinerTreeApproximator.h | 19 ------------------- delaunay/DelaunayGraph.cpp | 10 ++++------ delaunay/DelaunayGraph.h | 8 ++++---- delaunay/DelaunaySet.cpp | 3 --- dijkstra/DijkstraGraph.cpp | 7 ------- dijkstra/DijkstraGraph.h | 1 - graph/RectilinearGraph.h | 4 ++-- heap/BinaryHeap.h | 4 ++-- heap/FibonacciHeap.cpp | 8 -------- typedefs.h | 4 ++++ 18 files changed, 26 insertions(+), 74 deletions(-) delete mode 100644 SteinerTreeApproximator.cpp delete mode 100644 SteinerTreeApproximator.h diff --git a/.gitignore b/.gitignore index 2a5c48e..9e6ee22 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,6 @@ /4_filtered/ /.vscode/ /.idea/ +13_hard/ +bench_instances/ /PostScript/test.ps diff --git a/ArgumentHandler.h b/ArgumentHandler.h index 543b770..f81be72 100644 --- a/ArgumentHandler.h +++ b/ArgumentHandler.h @@ -17,12 +17,6 @@ enum struct OutputFormat { class ArgumentHandler { public: - struct OstreamWrapper { - explicit OstreamWrapper(std::ostream &o) : ostream(o) {} - - std::ostream &ostream; - }; - struct Parms { std::string input_filename; std::string output_filename; diff --git a/CMakeLists.txt b/CMakeLists.txt index 038518e..f8103ee 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,8 +7,6 @@ include_directories(.) add_executable(Hauptaufgabe_2 main.cpp - SteinerTreeApproximator.cpp - SteinerTreeApproximator.h STPFileParser.cpp STPFileParser.h dijkstra/DijkstraGraph.cpp dijkstra/DijkstraGraph.h diff --git a/MehlhornDelaunayGraphFactory.cpp b/MehlhornDelaunayGraphFactory.cpp index b27c1cd..c03cdde 100644 --- a/MehlhornDelaunayGraphFactory.cpp +++ b/MehlhornDelaunayGraphFactory.cpp @@ -59,7 +59,9 @@ Graph MehlhornDelaunayGraphFactory::create_delaunay_graph( std::optional current_mincost_edge = std::nullopt; for (auto candidate_edge: candidate_edges) { - if (not current_mincost_edge.has_value()) { current_mincost_edge = candidate_edge; } + if (not current_mincost_edge.has_value() + or current_mincost_edge->weight > + candidate_edge.weight) { current_mincost_edge = candidate_edge; } else if (not( current_mincost_edge->larger_terminal == candidate_edge.larger_terminal and @@ -71,8 +73,7 @@ Graph MehlhornDelaunayGraphFactory::create_delaunay_graph( current_mincost_edge->weight ); current_mincost_edge = candidate_edge; - } else if (current_mincost_edge->weight > - candidate_edge.weight) { current_mincost_edge = candidate_edge; } + } } edges.emplace_back( current_mincost_edge->smaller_terminal, diff --git a/MehlhornDelaunayGraphFactory.h b/MehlhornDelaunayGraphFactory.h index acc2ebe..6c82be5 100644 --- a/MehlhornDelaunayGraphFactory.h +++ b/MehlhornDelaunayGraphFactory.h @@ -9,11 +9,11 @@ #include "graph/Graph.h" #include "dijkstra/DijkstraGraph.h" -class MehlhornDelaunayGraphFactory { +class [[maybe_unused]] MehlhornDelaunayGraphFactory { public: - static Graph create_delaunay_graph(Graph const &graph); + [[maybe_unused]] static Graph create_delaunay_graph(Graph const &graph); static Graph create_delaunay_graph(DijkstraGraph const &dijkstra_graph, Graph const &graph); diff --git a/STPFileParser.cpp b/STPFileParser.cpp index 2306045..37cdfa7 100644 --- a/STPFileParser.cpp +++ b/STPFileParser.cpp @@ -81,7 +81,7 @@ STPFileParser::STPFileParser(std::string filename, size_t instances_to_skip) : _ } -Graph STPFileParser::create_graph() { +[[maybe_unused]] Graph STPFileParser::create_graph() { return {_num_nodes, _terminals, _edges}; } diff --git a/STPFileParser.h b/STPFileParser.h index 270d17e..ffc23a9 100644 --- a/STPFileParser.h +++ b/STPFileParser.h @@ -14,13 +14,11 @@ class STPFileParser { public: explicit STPFileParser(std::string filename, size_t instances_to_skip = 0); - Graph create_graph(); + [[maybe_unused]] Graph create_graph(); DelaunayGraph create_delaunay_graph(); private: - [[nodiscard]] std::ifstream open_input_file() const; - void read_graph_from_file(std::istream &file); void read_terminals_from_file(std::istream &file); diff --git a/SteinerTreeApproximator.cpp b/SteinerTreeApproximator.cpp deleted file mode 100644 index 345baa1..0000000 --- a/SteinerTreeApproximator.cpp +++ /dev/null @@ -1,5 +0,0 @@ -// -// Created by jgier on 29.04.2022. -// - -#include "SteinerTreeApproximator.h" diff --git a/SteinerTreeApproximator.h b/SteinerTreeApproximator.h deleted file mode 100644 index b14c410..0000000 --- a/SteinerTreeApproximator.h +++ /dev/null @@ -1,19 +0,0 @@ -// -// Created by jgier on 29.04.2022. -// - -#ifndef HAUPTAUFGABE_2_STEINERTREEAPPROXIMATOR_H -#define HAUPTAUFGABE_2_STEINERTREEAPPROXIMATOR_H - - -#include - -class [[maybe_unused]] SteinerTreeApproximator { -public: - explicit SteinerTreeApproximator(std::string filename); - - int calculate(); -}; - - -#endif //HAUPTAUFGABE_2_STEINERTREEAPPROXIMATOR_H diff --git a/delaunay/DelaunayGraph.cpp b/delaunay/DelaunayGraph.cpp index ad17132..094cc87 100644 --- a/delaunay/DelaunayGraph.cpp +++ b/delaunay/DelaunayGraph.cpp @@ -4,7 +4,6 @@ #include #include "DelaunayGraph.h" -#include "../heap/BinaryHeap.h" #include "DelaunayPriorityQueue.h" #include "DelaunaySet.h" #include @@ -72,7 +71,7 @@ void DelaunayGraph::update_inactivation_records(DelaunayPriorityQueue &X, const } } -void DelaunayGraph::primitive_print(std::ostream &os) { +[[maybe_unused]] void DelaunayGraph::primitive_print(std::ostream &os) { os << "Terminals: " << std::endl; for (auto const &terminal: _terminals) { os << "id: " << terminal.id << ", x: " << terminal.x_coord << ", y: " << terminal.y_coord << std::endl; @@ -96,8 +95,7 @@ Graph DelaunayGraph::export_graph() { ret.add_edge({ edge.terminal_a.x_coord + edge.terminal_a.y_coord * sqrt, edge.terminal_b.x_coord + edge.terminal_b.y_coord * sqrt, - std::abs(edge.terminal_a.x_coord - edge.terminal_b.x_coord) - + std::abs(edge.terminal_a.y_coord - edge.terminal_b.y_coord) + edge.terminal_a.distance(edge.terminal_b) }); } @@ -140,7 +138,7 @@ void DelaunayGraph::translate_from_infty_to_1_norm() { } } -void DelaunayGraph::print_as_postscript(std::ostream &os, const std::string &base_file_name) { +[[maybe_unused]] void DelaunayGraph::print_as_postscript(std::ostream &os, const std::string &base_file_name) { std::ifstream base_file(base_file_name); assert(base_file); std::string line; @@ -186,7 +184,7 @@ EdgeId DelaunayGraph::num_edges() const { return static_cast(_edges.size const std::vector &DelaunayGraph::edges() const { return _edges; } -NodeId DelaunayGraph::num_terminals() const { return _terminals.size(); } +NodeId DelaunayGraph::num_terminals() const { return static_cast(_terminals.size()); } void DelaunayGraph::add_edge(DelaunayGraph::Terminal terminal_a, DelaunayGraph::Terminal terminal_b) { _edges.push_back({terminal_a, terminal_b}); diff --git a/delaunay/DelaunayGraph.h b/delaunay/DelaunayGraph.h index 77e08d6..812e5ea 100644 --- a/delaunay/DelaunayGraph.h +++ b/delaunay/DelaunayGraph.h @@ -46,14 +46,14 @@ class DelaunayGraph { void add_terminal(GridUnit x_coord, GridUnit y_coord); void add_edge(Terminal terminal_a, Terminal terminal_b); void calculate(); - void primitive_print(std::ostream &os); + [[maybe_unused]] void primitive_print(std::ostream &os); void translate_from_1_to_infty_norm(); void translate_from_infty_to_1_norm(); Graph export_graph(); - void print_as_postscript(std::ostream &os, const std::string &base_file_name); + [[maybe_unused]] void print_as_postscript(std::ostream &os, const std::string &base_file_name); [[nodiscard]] NodeId num_terminals() const; [[nodiscard]] EdgeId num_edges() const; @@ -64,8 +64,8 @@ class DelaunayGraph { private: static void update_inactivation_records(DelaunayPriorityQueue &X, DelaunaySet const &Y, Terminal terminal); - void translate_terminal_from_1_to_infty_norm(Terminal &t); - void translate_terminal_from_infty_to_1_norm(Terminal &t); + static void translate_terminal_from_1_to_infty_norm(Terminal &t); + static void translate_terminal_from_infty_to_1_norm(Terminal &t); std::vector _terminals; std::vector _edges; diff --git a/delaunay/DelaunaySet.cpp b/delaunay/DelaunaySet.cpp index d684f0f..b429349 100644 --- a/delaunay/DelaunaySet.cpp +++ b/delaunay/DelaunaySet.cpp @@ -2,7 +2,6 @@ // Created by jgier on 27.06.2022. // -#include #include "DelaunaySet.h" @@ -18,12 +17,10 @@ void DelaunaySet::del(const DelaunaySet::Terminal &t) { std::optional DelaunaySet::predecessor(const DelaunaySet::Terminal &t) const { auto ret = *_greater_set.upper_bound(t); - //assert(greater_fun(t, ret)); return greater_fun(t, ret) ? std::make_optional(ret) : std::nullopt; } std::optional DelaunaySet::successor(const DelaunaySet::Terminal &t) const { auto ret = *_less_set.upper_bound(t); - //assert(less_fun(t, ret)); return less_fun(t, ret) ? std::make_optional(ret) : std::nullopt; } diff --git a/dijkstra/DijkstraGraph.cpp b/dijkstra/DijkstraGraph.cpp index 546f665..920797d 100644 --- a/dijkstra/DijkstraGraph.cpp +++ b/dijkstra/DijkstraGraph.cpp @@ -8,7 +8,6 @@ #include "../heap/FibonacciHeap.h" #include "../heap/StandardHeap.h" #include -#include DijkstraGraph::DijkstraGraph(Graph const &graph) : _calculation_finished(false) { @@ -32,8 +31,6 @@ const DijkstraGraph::Node &DijkstraGraph::operator[](NodeId index) const { void DijkstraGraph::dijkstras_algorithm(NodeId root_node_id) { - // auto id_to_node_projection = [this](auto const node_id) { return _nodes[node_id]; }; - if (root_node_id >= _nodes.size()) { throw std::invalid_argument("Root node not in graph."); } @@ -44,7 +41,6 @@ void DijkstraGraph::dijkstras_algorithm(NodeId root_node_id) { node.closest_terminal = -1; node.distance_to_root = -1; node.predecessor = -1; - node.included = false; } StandardHeap candidates = StandardHeap{}; @@ -61,7 +57,6 @@ void DijkstraGraph::dijkstras_algorithm(NodeId root_node_id) { while (!candidates.empty()) { NodeId node_v_id = candidates.extract_min(); Node &node_v = _nodes[node_v_id]; - node_v.included = true; // for (auto const& node_w : node_v.neighbours | std::views::transform(id_to_node_projection)) // Fände ich auch schöner so, aber CLion beschwert sich da irgendwie. Vielleicht habe ich da was falsch @@ -74,7 +69,6 @@ void DijkstraGraph::dijkstras_algorithm(NodeId root_node_id) { if (node_w.distance_to_root == -1) { - // TODO: extract method node_w.predecessor = node_v_id; node_w.distance_to_root = node_v.distance_to_root + edge_weight; @@ -95,7 +89,6 @@ void DijkstraGraph::dijkstras_algorithm(NodeId root_node_id) { // key in the prioritiy queue else if (node_w.distance_to_root > node_v.distance_to_root + edge_weight) { - // TODO: extract method node_w.distance_to_root = node_v.distance_to_root + edge_weight; candidates.decrease_key(node_w_id, node_w.distance_to_root); node_w.predecessor = node_v_id; diff --git a/dijkstra/DijkstraGraph.h b/dijkstra/DijkstraGraph.h index 84fbf5f..5afecec 100644 --- a/dijkstra/DijkstraGraph.h +++ b/dijkstra/DijkstraGraph.h @@ -20,7 +20,6 @@ class DijkstraGraph { NodeId predecessor = -1; NodeId closest_terminal = -1; WeightType distance_to_root = -1; - bool included = false; }; diff --git a/graph/RectilinearGraph.h b/graph/RectilinearGraph.h index f48127f..93abc0e 100644 --- a/graph/RectilinearGraph.h +++ b/graph/RectilinearGraph.h @@ -55,9 +55,9 @@ class RectilinearGraph { static void swap_if_a_larger(NodeId &node_a, NodeId &node_b); - int x_coord(NodeId node) const; + [[nodiscard]] int x_coord(NodeId node) const; - int y_coord(NodeId node) const; + [[nodiscard]] int y_coord(NodeId node) const; }; diff --git a/heap/BinaryHeap.h b/heap/BinaryHeap.h index 56b8f36..69b0b07 100644 --- a/heap/BinaryHeap.h +++ b/heap/BinaryHeap.h @@ -8,12 +8,12 @@ #include "Heap.h" -class BinaryHeap : public Heap { +class [[maybe_unused]] BinaryHeap : public Heap { public: void insert(IdType item_id, KeyType key) override; int extract_min() override; void decrease_key(int item_id, int new_key) override; - bool empty() const override; + [[nodiscard]] bool empty() const override; ~BinaryHeap() override; }; diff --git a/heap/FibonacciHeap.cpp b/heap/FibonacciHeap.cpp index 303461e..945864e 100644 --- a/heap/FibonacciHeap.cpp +++ b/heap/FibonacciHeap.cpp @@ -4,7 +4,6 @@ #include #include -#include #include "FibonacciHeap.h" FibonacciHeap::Node::Node(int item_id, int key) { @@ -71,13 +70,6 @@ int FibonacciHeap::extract_min() { } for (int i = 0; i < _roots.size(); i++) { if (_roots[i] != nullptr) { - - // These asserts are only sensible, if I find a notion of knowing the maximum possible index, - // istead of choosing an arbitrary (large) value i.e. 10000. - - // assert(_roots[i]->key >= 0 && _roots[i]->key <= 10000); - // assert(_roots[min_index]->key >= 0 && _roots[i]->key <= 10000); - if (_roots[i]->key < _roots[min_index]->key) { min_index = i; } diff --git a/typedefs.h b/typedefs.h index ad83703..3cfbcab 100644 --- a/typedefs.h +++ b/typedefs.h @@ -6,10 +6,14 @@ #define HAUPTAUFGABE_2_TYPEDEFS_H #include +#include + +//#define DEBUG using NodeId = int; using EdgeId = int; using GridUnit = int; using WeightType = int; + #endif //HAUPTAUFGABE_2_TYPEDEFS_H From f2a67b0c0c52ccf577ba03fbdf2d59ae8e3262a7 Mon Sep 17 00:00:00 2001 From: Jakob Gierschmann Date: Sun, 3 Jul 2022 22:30:09 +0200 Subject: [PATCH 17/25] refactoring and bugfixing, added -t and -s options --- .gitignore | 1 + ArgumentHandler.cpp | 38 +++++++++++++++--- ArgumentHandler.h | 4 +- STPFileParser.cpp | 82 ++++++++++++++++++++++---------------- STPFileParser.h | 9 ++++- delaunay/DelaunayGraph.cpp | 7 ++-- delaunay/DelaunayGraph.h | 18 +++++++-- graph/RectilinearGraph.cpp | 2 +- graph/RectilinearGraph.h | 2 +- kruskal/kruskal_ind.cpp | 15 +------ main.cpp | 12 ++++-- typedefs.h | 5 +++ 12 files changed, 127 insertions(+), 68 deletions(-) diff --git a/.gitignore b/.gitignore index 9e6ee22..e6d6228 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ 13_hard/ bench_instances/ /PostScript/test.ps +/Output/ \ No newline at end of file diff --git a/ArgumentHandler.cpp b/ArgumentHandler.cpp index b3dacb8..f0b246f 100644 --- a/ArgumentHandler.cpp +++ b/ArgumentHandler.cpp @@ -9,7 +9,15 @@ const ArgumentHandler::Parms &ArgumentHandler::parms() { return _parms; } -ArgumentHandler::ArgumentHandler(int argc, char **argv) : _parms{"", "", OutputFormat::STP, 0, false, true} { +ArgumentHandler::ArgumentHandler(int argc, char **argv) : _parms{ + "", + "", + OutputFormat::STP, + 0, + false, + false, + true +} { for (int i = 1; i < argc; i++) { std::string arg(argv[i]); if (arg == "-ps") { @@ -30,7 +38,18 @@ ArgumentHandler::ArgumentHandler(int argc, char **argv) : _parms{"", "", OutputF std::cout << "Specify output file: "; std::cin >> _parms.output_filename; } else if (arg == "-h" or arg == "--help") { - print_usage(argv[0]); + print_usage(std::cout, argv[0]); + _parms.run = false; + } else if (arg == "-t" or arg == "--nodes-are-terminals") { + _parms.nodes_are_terminals = true; + } else if (arg == "-s" or arg == "--skip-instances") { + i++; + if (i < argc) { + _parms.instances_to_skip = std::stoi(argv[i]); + } + } else if (arg[0] == '-') { + std::cerr << argv[0] << ": " << arg << ": invalid option" << std::endl; + print_usage(std::cerr, argv[0]); _parms.run = false; } else if (_parms.input_filename.empty()) { _parms.input_filename = arg; @@ -38,8 +57,15 @@ ArgumentHandler::ArgumentHandler(int argc, char **argv) : _parms{"", "", OutputF } } -void ArgumentHandler::print_usage(std::string const &program_name) { - std::cerr << "Usage: " << program_name - << " [-ps] [-stp] [-o OUTPUT_FILE] [-mi] [-mo] [-v] [INPUT_FILENAME]" - << std::endl; +void ArgumentHandler::print_usage(std::ostream &os, std::string const &program_name) { + os << "Usage: " << program_name + << " [-ps | -stp] [-o OUTPUT_FILE] [-mi] [-mo] [-v] [-t] [-s NUM] [INPUT_FILENAME]" + << std::endl << std::endl; + os << "Options: " << std::endl; + os << "\t -ps | -stp \t\t output result as PostScript or .stp respectively" << std::endl; + os << "\t -o OUTPUT_FILE \t specify file to write output to. Blank for standard output" << std::endl; + os << "\t -mi -mo \t\t Specify input respectively output file during runtime via standard input" << std::endl; + os << "\t -v \t\t\t print verbose information" << std::endl; + os << "\t -t \t\t\t treat nodes as terminals" << std::endl; + os << "\t -s NUMBER \t\t skip number of instances in input" << std::endl; } diff --git a/ArgumentHandler.h b/ArgumentHandler.h index f81be72..33eff6a 100644 --- a/ArgumentHandler.h +++ b/ArgumentHandler.h @@ -22,15 +22,17 @@ class ArgumentHandler { std::string output_filename; OutputFormat output_format; size_t instances_to_skip; + bool nodes_are_terminals; bool verbose; bool run; }; ArgumentHandler(int argc, char **argv); - static void print_usage(std::string const &program_name); + static void print_usage(std::ostream &os, std::string const &program_name); Parms const &parms(); + private: Parms _parms; std::optional _file; diff --git a/STPFileParser.cpp b/STPFileParser.cpp index 37cdfa7..41ea665 100644 --- a/STPFileParser.cpp +++ b/STPFileParser.cpp @@ -12,7 +12,7 @@ constexpr char FILE_HEADER[] = "33D32945 STP File, STP Format Version 1.0"; -STPFileParser::STPFileParser(std::string filename, size_t instances_to_skip) : _filename(std::move(filename)) { +STPFileParser::STPFileParser(ArgumentHandler::Parms const &parms) : _filename(std::move(parms.input_filename)) { _num_nodes = -1; @@ -25,21 +25,17 @@ STPFileParser::STPFileParser(std::string filename, size_t instances_to_skip) : _ // skip the required number of instances in the input std::string line; safe_getline(input, line); // read the first line of the input -// while (line.empty()) // find the first non-empty line -// { -// safe_getline(input, line); -// } - for (size_t i = 0; i < instances_to_skip + 1; i++) { + for (size_t i = 0; i < parms.instances_to_skip + 1; i++) { while (line != FILE_HEADER and not input.eof()) { safe_getline(input, line); remove_carriage_return(line); } + safe_getline(input, line); } - // check for valid input header -// if (line != FILE_HEADER) { -// throw std::invalid_argument("Invalid input format."); -// } + if (input.eof()) { + throw std::invalid_argument("Invalid input format: No stp-instance found"); + } bool graph_found = false; bool terminals_found = false; @@ -49,28 +45,31 @@ STPFileParser::STPFileParser(std::string filename, size_t instances_to_skip) : _ if (not safe_getline(input, line)) { throw std::invalid_argument("Invalid input format: EOF expected"); } - remove_carriage_return(line); - if (line == "SECTION Graph") { + if (line == "SECTION Graph" or line == "Section Graph") { if (graph_found) { throw std::invalid_argument("Invalid input format: Multiple graphs in input."); } else { read_graph_from_file(input); graph_found = true; } - } else if (line == "SECTION Terminals") { + } else if (line == "SECTION Terminals" or line == "Section Terminals") { if (terminals_found) { throw std::invalid_argument("Invalid input format: Multiple terminal sections in input."); } else { read_terminals_from_file(input); terminals_found = true; } - } else if (line == "SECTION Coordinates") { + } else if (line == "SECTION Coordinates" or line == "Section Coordinates") { if (coordinates_found) { throw std::invalid_argument("Invalid input format: Multiple coordinate sections in input."); } - _num_terminals = _num_nodes; - _num_nodes = 10000 * 10000; - read_coordinates_from_file(input); + if (parms.nodes_are_terminals) { + _num_terminals = _num_nodes; + _num_nodes = 10000 * 10000; + read_coordinates_from_file(input, true); + } else { + read_coordinates_from_file(input, false); + } coordinates_found = true; } } @@ -91,7 +90,7 @@ void STPFileParser::read_graph_from_file(std::istream &file) { int val_1, val_2, val_3; int num_edges = 0; - while (line != "END") { + while (line != "END" and line != "End") { if (!safe_getline(file, line) or line == "EOF" or line == "EOF\r") { throw std::invalid_argument("Invalid file format."); } @@ -122,9 +121,9 @@ void STPFileParser::read_terminals_from_file(std::istream &file) { std::string specifier; int val; - while (line != "END") { + while (line != "END" and line != "End") { if (!safe_getline(file, line) or line == "EOF" or line == "EOF\r") { - throw std::invalid_argument("Invalid file format."); + throw std::invalid_argument("Invalid file format: END expected"); } std::stringstream ss(line); ss >> specifier >> val; @@ -141,24 +140,34 @@ void STPFileParser::read_terminals_from_file(std::istream &file) { } } -void STPFileParser::read_coordinates_from_file(std::istream &file) { +void STPFileParser::read_coordinates_from_file(std::istream &file, bool nodes_are_terminals) { std::string line; std::string specifier; - int terminal_id, x_coord, y_coord; - while (line != "END") { - if (!safe_getline(file, line) or line == "EOF" or line == "EOF\r") { - throw std::invalid_argument("Invalid file format."); + int node_id, x_coord, y_coord; + while (line != "END" and line != "End") { + if (!safe_getline(file, line) or line == "EOF") { + throw std::invalid_argument("Invalid file format: END expected"); } - remove_carriage_return(line); std::stringstream ss(line); - ss >> specifier >> terminal_id >> x_coord >> y_coord; + ss >> specifier >> node_id >> x_coord >> y_coord; + + if (not nodes_are_terminals) { + _node_coords.resize(_num_nodes); + } + if (specifier == "DD") { // TODO where do the coordiantes begin, at 0,0 or 1,1? - _terminals.push_back((y_coord /* -1 */) * 10000 + x_coord /* -1 */ ); + if (nodes_are_terminals) { + _terminals.push_back((y_coord /* -1 */) * 10000 + x_coord /* -1 */ ); + } else { + _node_coords[node_id] = {x_coord, y_coord}; + } } } - if ((size_t) _num_terminals != _terminals.size()) { - throw std::invalid_argument("Invalid file format. Wrong number of terminals."); + if (nodes_are_terminals) { + if ((size_t) _num_terminals != _terminals.size()) { + throw std::invalid_argument("Invalid file format. Wrong number of terminals."); + } } } @@ -171,18 +180,21 @@ inline void STPFileParser::remove_carriage_return(std::string &s) { DelaunayGraph STPFileParser::create_delaunay_graph() { DelaunayGraph delaunay_graph; auto sqrt = static_cast(std::sqrt(_num_nodes)); - assert(sqrt * sqrt == _num_nodes); + if (_node_coords.empty()) { + assert(sqrt * sqrt == _num_nodes); + } for (auto terminal_id: _terminals) { - delaunay_graph.add_terminal(terminal_id % sqrt, static_cast(terminal_id / sqrt)); + if (_node_coords.empty()) { + delaunay_graph.add_terminal(terminal_id % sqrt, static_cast(terminal_id / sqrt)); + } else { + delaunay_graph.add_terminal(_node_coords[terminal_id].x, _node_coords[terminal_id].y); + } } return delaunay_graph; } inline std::istream &STPFileParser::safe_getline(std::istream &istream, std::string &string) { auto &ret = std::getline(istream, string); - if (not ret or istream.eof()) { - throw std::invalid_argument("invalid input format expected EOF"); - } remove_carriage_return(string); return ret; } diff --git a/STPFileParser.h b/STPFileParser.h index ffc23a9..8a36890 100644 --- a/STPFileParser.h +++ b/STPFileParser.h @@ -9,12 +9,15 @@ #include #include "graph/Graph.h" #include "delaunay/DelaunayGraph.h" +#include "ArgumentHandler.h" + class STPFileParser { public: - explicit STPFileParser(std::string filename, size_t instances_to_skip = 0); + explicit STPFileParser(ArgumentHandler::Parms const &parms); [[maybe_unused]] Graph create_graph(); + DelaunayGraph create_delaunay_graph(); private: @@ -23,16 +26,18 @@ class STPFileParser { void read_terminals_from_file(std::istream &file); - void read_coordinates_from_file(std::istream &file); + void read_coordinates_from_file(std::istream &file, bool nodes_are_terminals); static void remove_carriage_return(std::string &s); static std::istream &safe_getline(std::istream &istream, std::string &string); + std::string _filename; NodeId _num_nodes; NodeId _num_terminals; std::vector _terminals; std::vector _edges; + std::vector _node_coords; }; diff --git a/delaunay/DelaunayGraph.cpp b/delaunay/DelaunayGraph.cpp index 094cc87..1efe437 100644 --- a/delaunay/DelaunayGraph.cpp +++ b/delaunay/DelaunayGraph.cpp @@ -10,8 +10,8 @@ #include #include -void DelaunayGraph::add_terminal(GridUnit x_coord, GridUnit y_coord) { - _terminals.emplace_back(x_coord, y_coord, _terminals.size()); +void DelaunayGraph::add_terminal(GridUnit x_coord, GridUnit y_coord, NodeId terminal_id) { + _terminals.emplace_back(x_coord, y_coord, _terminals.size(), terminal_id); _max_x = std::max(_max_x, std::abs(x_coord)); _max_y = std::max(_max_y, std::abs(y_coord)); _orig_max_x = _max_x; @@ -74,7 +74,8 @@ void DelaunayGraph::update_inactivation_records(DelaunayPriorityQueue &X, const [[maybe_unused]] void DelaunayGraph::primitive_print(std::ostream &os) { os << "Terminals: " << std::endl; for (auto const &terminal: _terminals) { - os << "id: " << terminal.id << ", x: " << terminal.x_coord << ", y: " << terminal.y_coord << std::endl; + os << "internal_id: " << terminal.internal_id << ", x: " << terminal.x_coord << ", y: " << terminal.y_coord + << std::endl; } os << std::endl << "Edges: " << std::endl; for (auto const &edge: _edges) { diff --git a/delaunay/DelaunayGraph.h b/delaunay/DelaunayGraph.h index 812e5ea..950cd55 100644 --- a/delaunay/DelaunayGraph.h +++ b/delaunay/DelaunayGraph.h @@ -22,11 +22,13 @@ class DelaunayGraph { DelaunayGraph() : _max_x(0), _max_y(0), _orig_max_x(0), _orig_max_y(0) {} struct Terminal { - Terminal(GridUnit x_coord, GridUnit y_coord, NodeId id = -1) : x_coord(x_coord), y_coord(y_coord), id(id) {} + Terminal(GridUnit x_coord, GridUnit y_coord, NodeId internal_id = -1, NodeId node_id = -1) + : x_coord(x_coord), y_coord(y_coord), internal_id(internal_id), node_id(node_id) {} GridUnit x_coord; GridUnit y_coord; - NodeId id; + NodeId internal_id; // unique ascending identifier + NodeId node_id; // original node id bool operator==(Terminal const &other) const; @@ -40,15 +42,20 @@ class DelaunayGraph { struct Edge { Terminal terminal_a; Terminal terminal_b; + [[nodiscard]] bool operator<(Edge const &other) const; }; - void add_terminal(GridUnit x_coord, GridUnit y_coord); + void add_terminal(GridUnit x_coord, GridUnit y_coord, NodeId terminal_id = -1); + void add_edge(Terminal terminal_a, Terminal terminal_b); + void calculate(); + [[maybe_unused]] void primitive_print(std::ostream &os); void translate_from_1_to_infty_norm(); + void translate_from_infty_to_1_norm(); Graph export_graph(); @@ -56,15 +63,20 @@ class DelaunayGraph { [[maybe_unused]] void print_as_postscript(std::ostream &os, const std::string &base_file_name); [[nodiscard]] NodeId num_terminals() const; + [[nodiscard]] EdgeId num_edges() const; + [[nodiscard]] std::vector const &terminals() const; + [[nodiscard]] std::vector const &edges() const; private: static void update_inactivation_records(DelaunayPriorityQueue &X, DelaunaySet const &Y, Terminal terminal); + static void translate_terminal_from_1_to_infty_norm(Terminal &t); + static void translate_terminal_from_infty_to_1_norm(Terminal &t); std::vector _terminals; diff --git a/graph/RectilinearGraph.cpp b/graph/RectilinearGraph.cpp index e6d1215..4664ba9 100644 --- a/graph/RectilinearGraph.cpp +++ b/graph/RectilinearGraph.cpp @@ -62,7 +62,7 @@ std::optional RectilinearGraph::get_horizontal_connecting_edge(NodeId no } else return std::nullopt; } -std::optional RectilinearGraph::get_vertical_conecting_edge(NodeId node_a, NodeId node_b) const { +std::optional RectilinearGraph::get_vertical_connecting_edge(NodeId node_a, NodeId node_b) const { swap_if_a_larger(node_a, node_b); if (node_a % _grid_width == node_b % _grid_width) { diff --git a/graph/RectilinearGraph.h b/graph/RectilinearGraph.h index 93abc0e..2012f62 100644 --- a/graph/RectilinearGraph.h +++ b/graph/RectilinearGraph.h @@ -49,7 +49,7 @@ class RectilinearGraph { get_horizontal_connecting_edge(NodeId node_a, NodeId node_b) const; [[maybe_unused]] [[nodiscard]] std::optional - get_vertical_conecting_edge(NodeId node_a, NodeId node_b) const; + get_vertical_connecting_edge(NodeId node_a, NodeId node_b) const; [[nodiscard]] bool validate_node(NodeId node) const; diff --git a/kruskal/kruskal_ind.cpp b/kruskal/kruskal_ind.cpp index 22bca3e..f476cee 100644 --- a/kruskal/kruskal_ind.cpp +++ b/kruskal/kruskal_ind.cpp @@ -8,7 +8,6 @@ Graph kruskal(const Graph &input_graph) { Graph return_graph(input_graph.num_nodes(), input_graph.terminals()); - // vector erzeugen und mit den Zahlen [ 0, edges.size() ) fuellen std::vector edge_ids(input_graph.num_edges()); std::iota(edge_ids.begin(), edge_ids.end(), 0); @@ -18,11 +17,6 @@ Graph kruskal(const Graph &input_graph) { [&id_to_edge_projection](EdgeId a, EdgeId b) { return (id_to_edge_projection(a) < id_to_edge_projection(b)); }); - // iteriere ueber die edges in der reihenfolge, wie sie im id-vector gespeichert sind - // diese syntax range | adaptor, bedeutet, dass die range (hier edge_ids) erst durch die adaptor-funktion gejagt wird - // (transform ruft die uebergebene funktion einmal mit jedem element auf und ist dann eine range ueber die return-values der funktion) - // es gibt z.b. auch std::views::filter(unary_predicate), was dann nur die elemente durchlaesst, fuer die das unaere praedikat true zurueckgibt - Disjoint_Set set; set.make_sets(return_graph.num_nodes()); for (auto edge_id: edge_ids) { @@ -50,19 +44,14 @@ DelaunayGraph kruskal(DelaunayGraph const &delaunay_graph) { [&id_to_edge_projection](EdgeId a, EdgeId b) { return (id_to_edge_projection(a) < id_to_edge_projection(b)); }); - // iteriere ueber die edges in der reihenfolge, wie sie im id-vector gespeichert sind - // diese syntax range | adaptor, bedeutet, dass die range (hier edge_ids) erst durch die adaptor-funktion gejagt wird - // (transform ruft die uebergebene funktion einmal mit jedem element auf und ist dann eine range ueber die return-values der funktion) - // es gibt z.b. auch std::views::filter(unary_predicate), was dann nur die elemente durchlaesst, fuer die das unaere praedikat true zurueckgibt - Disjoint_Set set; set.make_sets(return_graph.num_terminals()); for (auto edge_id: edge_ids) { auto const &edge = id_to_edge_projection(edge_id); - if (!set.set_equals(edge.terminal_a.id, edge.terminal_b.id)) { + if (!set.set_equals(edge.terminal_a.internal_id, edge.terminal_b.internal_id)) { // unite the sets of the parents of the nodes of edge - set.unite(edge.terminal_a.id, edge.terminal_b.id); + set.unite(edge.terminal_a.internal_id, edge.terminal_b.internal_id); // add edge to the vector of included edges return_graph.add_edge(edge.terminal_a, edge.terminal_b); } diff --git a/main.cpp b/main.cpp index d772999..704b9fd 100644 --- a/main.cpp +++ b/main.cpp @@ -12,10 +12,16 @@ int main(int argc, char *argv[]) { auto print_verbose = [& parms](std::string const &s) { if (parms.verbose) std::cout << s << std::endl; }; - STPFileParser parser(parms.input_filename, parms.instances_to_skip); - print_verbose("input parsed"); + DelaunayGraph delaunay_graph; + try { + STPFileParser parser(parms); + print_verbose("input parsed"); + delaunay_graph = parser.create_delaunay_graph(); + } catch (std::invalid_argument const &e) { + std::cerr << "Failed to read input. Error: " << e.what() << std::endl; + return 1; + } - DelaunayGraph delaunay_graph = parser.create_delaunay_graph(); delaunay_graph.translate_from_1_to_infty_norm(); delaunay_graph.calculate(); delaunay_graph.translate_from_infty_to_1_norm(); diff --git a/typedefs.h b/typedefs.h index 3cfbcab..ddf332c 100644 --- a/typedefs.h +++ b/typedefs.h @@ -15,5 +15,10 @@ using EdgeId = int; using GridUnit = int; using WeightType = int; +struct Coord { + GridUnit x; + GridUnit y; +}; + #endif //HAUPTAUFGABE_2_TYPEDEFS_H From 3f2720fdee207f5a15ff2e34b53962ddab569113 Mon Sep 17 00:00:00 2001 From: JakobGie <44549596+armytrong@users.noreply.github.com> Date: Mon, 4 Jul 2022 23:39:21 +0200 Subject: [PATCH 18/25] added ability to add steiner nodes to delaunay graph --- CMakeLists.txt | 16 ++- ...aphFactory.cpp => MehlhornGraphFactory.cpp | 14 +- ...ayGraphFactory.h => MehlhornGraphFactory.h | 12 +- STPFileParser.cpp | 8 +- delaunay/DelaunayGraph.cpp | 132 +++++++++++------- delaunay/DelaunayGraph.h | 56 ++++---- delaunay/DelaunayPriorityQueue.h | 2 +- delaunay/DelaunaySet.h | 2 +- dijkstra/DijkstraGraph.cpp | 4 +- kruskal/kruskal_ind.cpp | 10 +- main.cpp | 7 +- 11 files changed, 155 insertions(+), 108 deletions(-) rename MehlhornDelaunayGraphFactory.cpp => MehlhornGraphFactory.cpp (86%) rename MehlhornDelaunayGraphFactory.h => MehlhornGraphFactory.h (64%) diff --git a/CMakeLists.txt b/CMakeLists.txt index f8103ee..c44f57d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,12 +14,22 @@ add_executable(Hauptaufgabe_2 heap/FibonacciHeap.cpp heap/FibonacciHeap.h typedefs.h - MehlhornDelaunayGraphFactory.cpp - MehlhornDelaunayGraphFactory.h + MehlhornGraphFactory.cpp + MehlhornGraphFactory.h kruskal/kruskal_ind.cpp kruskal/kruskal_ind.h heap/StandardHeap.cpp heap/StandardHeap.h graph/RectilinearGraph.cpp graph/RectilinearGraph.h - heap/BinaryHeap.cpp heap/BinaryHeap.h heap/Heap.h delaunay/DelaunayGraph.cpp delaunay/DelaunayGraph.h delaunay/DelaunaySet.cpp delaunay/DelaunaySet.h delaunay/DelaunayPriorityQueue.cpp delaunay/DelaunayPriorityQueue.h ArgumentHandler.cpp ArgumentHandler.h) + heap/BinaryHeap.cpp + heap/BinaryHeap.h + heap/Heap.h + delaunay/DelaunayGraph.cpp + delaunay/DelaunayGraph.h + delaunay/DelaunaySet.cpp + delaunay/DelaunaySet.h + delaunay/DelaunayPriorityQueue.cpp + delaunay/DelaunayPriorityQueue.h + ArgumentHandler.cpp + ArgumentHandler.h) diff --git a/MehlhornDelaunayGraphFactory.cpp b/MehlhornGraphFactory.cpp similarity index 86% rename from MehlhornDelaunayGraphFactory.cpp rename to MehlhornGraphFactory.cpp index c03cdde..f2c1b4b 100644 --- a/MehlhornDelaunayGraphFactory.cpp +++ b/MehlhornGraphFactory.cpp @@ -4,12 +4,12 @@ #include #include -#include "MehlhornDelaunayGraphFactory.h" +#include "MehlhornGraphFactory.h" void -MehlhornDelaunayGraphFactory::bucket_sort( +MehlhornGraphFactory::bucket_sort( std::vector &vector, - MehlhornDelaunayGraphFactory::TypeToNodeId type_to_node_id, + MehlhornGraphFactory::TypeToNodeId type_to_node_id, NodeId max_id) { std::vector> bucket_list; bucket_list.resize(max_id); @@ -25,7 +25,7 @@ MehlhornDelaunayGraphFactory::bucket_sort( } } -Graph MehlhornDelaunayGraphFactory::create_delaunay_graph( +Graph MehlhornGraphFactory::create_melhorn_graph( DijkstraGraph const &dijkstra_graph, Graph const &graph ) { @@ -84,14 +84,14 @@ Graph MehlhornDelaunayGraphFactory::create_delaunay_graph( return {graph.num_nodes(), graph.terminals(), edges}; } -Graph MehlhornDelaunayGraphFactory::create_delaunay_graph(const Graph &graph) { +Graph MehlhornGraphFactory::create_melhorn_graph(const Graph &graph) { DijkstraGraph dijkstra_graph(graph); NodeId root_node = dijkstra_graph.add_node(graph.terminals(), std::vector(graph.terminals().size(), 0)); dijkstra_graph.dijkstras_algorithm(root_node); - return create_delaunay_graph(dijkstra_graph, graph); + return create_melhorn_graph(dijkstra_graph, graph); } -MehlhornDelaunayGraphFactory::CandidateEdge::CandidateEdge(NodeId terminal_1, NodeId terminal_2, WeightType weight) : +MehlhornGraphFactory::CandidateEdge::CandidateEdge(NodeId terminal_1, NodeId terminal_2, WeightType weight) : smaller_terminal(std::min(terminal_1, terminal_2)), larger_terminal(std::max(terminal_1, terminal_2)), weight(weight) {} diff --git a/MehlhornDelaunayGraphFactory.h b/MehlhornGraphFactory.h similarity index 64% rename from MehlhornDelaunayGraphFactory.h rename to MehlhornGraphFactory.h index 6c82be5..f0694d6 100644 --- a/MehlhornDelaunayGraphFactory.h +++ b/MehlhornGraphFactory.h @@ -2,20 +2,20 @@ // Created by jgier on 20.06.2022. // -#ifndef HAUPTAUFGABE_2_MEHLHORNDELAUNAYGRAPHFACTORY_H -#define HAUPTAUFGABE_2_MEHLHORNDELAUNAYGRAPHFACTORY_H +#ifndef HAUPTAUFGABE_2_MEHLHORNGRAPHFACTORY_H +#define HAUPTAUFGABE_2_MEHLHORNGRAPHFACTORY_H #include "graph/Graph.h" #include "dijkstra/DijkstraGraph.h" -class [[maybe_unused]] MehlhornDelaunayGraphFactory { +class [[maybe_unused]] MehlhornGraphFactory { public: - [[maybe_unused]] static Graph create_delaunay_graph(Graph const &graph); + [[maybe_unused]] static Graph create_melhorn_graph(Graph const &graph); - static Graph create_delaunay_graph(DijkstraGraph const &dijkstra_graph, Graph const &graph); + static Graph create_melhorn_graph(DijkstraGraph const &dijkstra_graph, Graph const &graph); private: struct CandidateEdge { @@ -36,4 +36,4 @@ class [[maybe_unused]] MehlhornDelaunayGraphFactory { }; -#endif //HAUPTAUFGABE_2_MEHLHORNDELAUNAYGRAPHFACTORY_H +#endif //HAUPTAUFGABE_2_MEHLHORNGRAPHFACTORY_H diff --git a/STPFileParser.cpp b/STPFileParser.cpp index 41ea665..0ef7a77 100644 --- a/STPFileParser.cpp +++ b/STPFileParser.cpp @@ -106,7 +106,7 @@ void STPFileParser::read_graph_from_file(std::istream &file) { throw std::invalid_argument("Invalid file foramt: Invalid edge."); } // TODO Hier sollte man auch gegen values die trotzdem 0 sind guarden - // subtract 1 from node_id, this program is 0-based + // subtract 1 from original_node_id, this program is 0-based _edges.emplace_back(val_1 - 1, val_2 - 1, val_3); } } @@ -131,7 +131,7 @@ void STPFileParser::read_terminals_from_file(std::istream &file) { _terminals.reserve(val); _num_terminals = val; } else if (specifier == "T") { - // subtract 1 from node_id, this program is 0-based + // subtract 1 from original_node_id, this program is 0-based _terminals.push_back(val - 1); } } @@ -185,9 +185,9 @@ DelaunayGraph STPFileParser::create_delaunay_graph() { } for (auto terminal_id: _terminals) { if (_node_coords.empty()) { - delaunay_graph.add_terminal(terminal_id % sqrt, static_cast(terminal_id / sqrt)); + delaunay_graph.add_node(terminal_id % sqrt, static_cast(terminal_id / sqrt)); } else { - delaunay_graph.add_terminal(_node_coords[terminal_id].x, _node_coords[terminal_id].y); + delaunay_graph.add_node(_node_coords[terminal_id].x, _node_coords[terminal_id].y); } } return delaunay_graph; diff --git a/delaunay/DelaunayGraph.cpp b/delaunay/DelaunayGraph.cpp index 1efe437..d119082 100644 --- a/delaunay/DelaunayGraph.cpp +++ b/delaunay/DelaunayGraph.cpp @@ -10,25 +10,35 @@ #include #include -void DelaunayGraph::add_terminal(GridUnit x_coord, GridUnit y_coord, NodeId terminal_id) { - _terminals.emplace_back(x_coord, y_coord, _terminals.size(), terminal_id); +void DelaunayGraph::add_node(GridUnit x_coord, GridUnit y_coord, NodeId node_id) { + _nodes.emplace_back(x_coord, y_coord, _nodes.size(), node_id); _max_x = std::max(_max_x, std::abs(x_coord)); _max_y = std::max(_max_y, std::abs(y_coord)); _orig_max_x = _max_x; _orig_max_y = _max_y; +} +void DelaunayGraph::add_terminal(GridUnit x_coord, GridUnit y_coord, NodeId node_id) { + if (_nodes.size() == _num_terminals) { + _nodes.emplace_back(x_coord, y_coord, _nodes.size(), node_id); + } else { + _nodes.emplace(_nodes.begin() + _num_terminals, x_coord, y_coord, _nodes.size(), node_id); + } + _num_terminals += 1; } -void DelaunayGraph::calculate() { +void DelaunayGraph::calculate_l1_delaunay_triangulation() { + translate_from_1_to_infty_norm(); + DelaunayPriorityQueue X(_max_x, _max_y); - for (auto const &terminal: _terminals) { + for (auto const &terminal: _nodes) { X.insert(terminal, terminal.x_coord, ACTIVE); } DelaunaySet Y; Y.insert({-_max_x - 1, -_max_y - 1}); Y.insert({-_max_x - 1, _max_y + 1}); - std::map ages; + std::map ages; int age_counter = 0; while (not X.empty()) { @@ -56,10 +66,12 @@ void DelaunayGraph::calculate() { update_inactivation_records(X, Y, ages[*q] >= ages[*successor] ? *successor : *q); } } + + translate_from_infty_to_1_norm(); } void DelaunayGraph::update_inactivation_records(DelaunayPriorityQueue &X, const DelaunaySet &Y, - DelaunayGraph::Terminal terminal) { + DelaunayGraph::Node terminal) { auto r = Y.predecessor(terminal); auto q = Y.successor(terminal); if (r->x_coord > terminal.x_coord and q->x_coord >= terminal.x_coord) { @@ -71,46 +83,33 @@ void DelaunayGraph::update_inactivation_records(DelaunayPriorityQueue &X, const } } -[[maybe_unused]] void DelaunayGraph::primitive_print(std::ostream &os) { - os << "Terminals: " << std::endl; - for (auto const &terminal: _terminals) { - os << "internal_id: " << terminal.internal_id << ", x: " << terminal.x_coord << ", y: " << terminal.y_coord - << std::endl; - } - os << std::endl << "Edges: " << std::endl; - for (auto const &edge: _edges) { - os << edge.terminal_a.x_coord << " " << edge.terminal_a.y_coord << " -- " << edge.terminal_b.x_coord << " " - << edge.terminal_b.y_coord << std::endl; - } -} - Graph DelaunayGraph::export_graph() { NodeId sqrt = std::max(_orig_max_x, _orig_max_y) + 1; NodeId num_nodes = sqrt * sqrt; std::vector terminals; - for (auto const &terminal: _terminals) { + for (auto const &terminal: _nodes) { terminals.push_back(terminal.x_coord + terminal.y_coord * sqrt); } Graph ret{num_nodes, terminals}; for (auto const &edge: _edges) { ret.add_edge({ - edge.terminal_a.x_coord + edge.terminal_a.y_coord * sqrt, - edge.terminal_b.x_coord + edge.terminal_b.y_coord * sqrt, - edge.terminal_a.distance(edge.terminal_b) + edge.node_a.x_coord + edge.node_a.y_coord * sqrt, + edge.node_b.x_coord + edge.node_b.y_coord * sqrt, + edge.node_a.distance(edge.node_b) }); } return ret; } -void DelaunayGraph::translate_terminal_from_1_to_infty_norm(DelaunayGraph::Terminal &t) { +void DelaunayGraph::translate_terminal_from_1_to_infty_norm(DelaunayGraph::Node &t) { GridUnit new_x = t.x_coord + t.y_coord; GridUnit new_y = t.x_coord - t.y_coord; t.x_coord = new_x; t.y_coord = new_y; } -void DelaunayGraph::translate_terminal_from_infty_to_1_norm(DelaunayGraph::Terminal &t) { +void DelaunayGraph::translate_terminal_from_infty_to_1_norm(DelaunayGraph::Node &t) { GridUnit new_x = (t.x_coord + t.y_coord) / 2; GridUnit new_y = t.x_coord - new_x; t.x_coord = new_x; @@ -118,24 +117,24 @@ void DelaunayGraph::translate_terminal_from_infty_to_1_norm(DelaunayGraph::Termi } void DelaunayGraph::translate_from_1_to_infty_norm() { - for (auto &terminal: _terminals) { + for (auto &terminal: _nodes) { translate_terminal_from_1_to_infty_norm(terminal); _max_x = std::max(std::abs(terminal.x_coord), _max_x); _max_y = std::max(std::abs(terminal.y_coord), _max_y); } for (auto &edge: _edges) { - translate_terminal_from_1_to_infty_norm(edge.terminal_a); - translate_terminal_from_1_to_infty_norm(edge.terminal_b); + translate_terminal_from_1_to_infty_norm(edge.node_a); + translate_terminal_from_1_to_infty_norm(edge.node_b); } } void DelaunayGraph::translate_from_infty_to_1_norm() { - for (auto &terminal: _terminals) { + for (auto &terminal: _nodes) { translate_terminal_from_infty_to_1_norm(terminal); } for (auto &edge: _edges) { - translate_terminal_from_infty_to_1_norm(edge.terminal_a); - translate_terminal_from_infty_to_1_norm(edge.terminal_b); + translate_terminal_from_infty_to_1_norm(edge.node_a); + translate_terminal_from_infty_to_1_norm(edge.node_b); } } @@ -157,8 +156,8 @@ void DelaunayGraph::translate_from_infty_to_1_norm() { std::endl << std::endl; - os << _terminals.size() << " DefineTerminals" << std::endl; - for (auto terminal: _terminals) { + os << _nodes.size() << " DefineTerminals" << std::endl; + for (auto terminal: _nodes) { os << "\t" << terminal.x_coord + 1 << "\t" << terminal.y_coord + 1 << "\tDT" << std::endl; } @@ -168,10 +167,10 @@ void DelaunayGraph::translate_from_infty_to_1_norm() { os << "BeginPlot" << std::endl; os << "\tPlot_Terminals" << std::endl; for (auto const &edge: _edges) { - os << "\t" << edge.terminal_a.x_coord + 1 - << "\t" << edge.terminal_a.y_coord + 1 - << "\t" << edge.terminal_b.x_coord + 1 - << "\t" << edge.terminal_b.y_coord + 1 << "\tS" << std::endl; + os << "\t" << edge.node_a.x_coord + 1 + << "\t" << edge.node_a.y_coord + 1 + << "\t" << edge.node_b.x_coord + 1 + << "\t" << edge.node_b.y_coord + 1 << "\tS" << std::endl; } //os << " (Steiner Minimal Tree: " << _terminals.size() << "points, length=" << grid_width * grid_widht << ")" << @@ -179,40 +178,79 @@ void DelaunayGraph::translate_from_infty_to_1_norm() { os << "EndPlot" << std::endl; } -const std::vector &DelaunayGraph::terminals() const { return _terminals; } +const std::vector &DelaunayGraph::nodes() const { return _nodes; } EdgeId DelaunayGraph::num_edges() const { return static_cast(_edges.size()); } const std::vector &DelaunayGraph::edges() const { return _edges; } -NodeId DelaunayGraph::num_terminals() const { return static_cast(_terminals.size()); } +NodeId DelaunayGraph::num_nodes() const { return static_cast(_nodes.size()); } -void DelaunayGraph::add_edge(DelaunayGraph::Terminal terminal_a, DelaunayGraph::Terminal terminal_b) { +void DelaunayGraph::add_edge(DelaunayGraph::Node terminal_a, DelaunayGraph::Node terminal_b) { _edges.push_back({terminal_a, terminal_b}); } -DelaunayGraph::DelaunayGraph(std::vector terminals) : - _max_x(0), _max_y(0), _orig_max_x(0), _orig_max_y(0), _terminals(std::move(terminals)) { - for (auto const &terminal: _terminals) { +DelaunayGraph::DelaunayGraph(std::vector terminals) : + _max_x(0), _max_y(0), _orig_max_x(0), _orig_max_y(0), _nodes(std::move(terminals)) { + for (auto const &terminal: _nodes) { _max_x = std::max(std::abs(terminal.x_coord), _max_x); _max_y = std::max(std::abs(terminal.y_coord), _max_y); _orig_max_x = _max_x; _orig_max_y = _max_y; } + _num_terminals = static_cast(_nodes.size()); } -bool DelaunayGraph::Terminal::operator==(const DelaunayGraph::Terminal &other) const { +void DelaunayGraph::add_steiner_points() { + std::vector> edges_by_node_id(num_nodes()); + + for (auto const &edge: edges()) { + edges_by_node_id[edge.node_a.internal_id].emplace_back(edge); + edges_by_node_id[edge.node_b.internal_id].emplace_back(edge); + } + + auto const mid = [](GridUnit a, GridUnit b, GridUnit c) { + return std::max( + std::max(std::min(a, b), std::min(a, c)), + std::min(b, c)); + }; + + const NodeId orig_num_nodes = num_nodes(); + for (NodeId node_id = 0; node_id < orig_num_nodes; node_id++) { + auto const &node = _nodes[node_id]; + auto const &edges = edges_by_node_id[node.internal_id]; + for (int j = 0; j < edges.size(); j++) { + auto const &edge_a = edges[j]; + auto const &node_a = edge_a.node_a == node ? edge_a.node_b : edge_a.node_a; + for (int k = j + 1; k < edges.size(); k++) { + auto const &edge_b = edges[k]; + auto const &node_b = edge_b.node_a == node ? edge_b.node_b : edge_b.node_a; + + auto const center_x = mid(node.x_coord, node_a.x_coord, node_b.x_coord); + auto const center_y = mid(node.y_coord, node_a.y_coord, node_b.y_coord); + + add_node(center_x, center_y); + } + } + } +} + +bool DelaunayGraph::Node::operator==(const DelaunayGraph::Node &other) const { return x_coord == other.x_coord and y_coord == other.y_coord; } -bool DelaunayGraph::Terminal::operator<(const DelaunayGraph::Terminal &other) const { +bool DelaunayGraph::Node::operator<(const DelaunayGraph::Node &other) const { return x_coord < other.x_coord or (x_coord == other.x_coord and y_coord < other.y_coord); } -GridUnit DelaunayGraph::Terminal::distance(const DelaunayGraph::Terminal &other) const { +GridUnit DelaunayGraph::Node::distance(const DelaunayGraph::Node &other) const { return std::abs(x_coord - other.x_coord) + std::abs(y_coord - other.y_coord); } bool DelaunayGraph::Edge::operator<(const DelaunayGraph::Edge &other) const { - return terminal_a.distance(terminal_b) < other.terminal_a.distance((other.terminal_b)); + return node_a.distance(node_b) < other.node_a.distance((other.node_b)); +} + +bool DelaunayGraph::Edge::operator==(Edge const &other) const { + return node_a == other.node_a and node_b == other.node_b; } diff --git a/delaunay/DelaunayGraph.h b/delaunay/DelaunayGraph.h index 950cd55..c9deb7b 100644 --- a/delaunay/DelaunayGraph.h +++ b/delaunay/DelaunayGraph.h @@ -19,69 +19,67 @@ class DelaunaySet; class DelaunayGraph { public: - DelaunayGraph() : _max_x(0), _max_y(0), _orig_max_x(0), _orig_max_y(0) {} - - struct Terminal { - Terminal(GridUnit x_coord, GridUnit y_coord, NodeId internal_id = -1, NodeId node_id = -1) - : x_coord(x_coord), y_coord(y_coord), internal_id(internal_id), node_id(node_id) {} + struct Node { + Node(GridUnit x_coord, GridUnit y_coord, NodeId internal_id = -1, NodeId node_id = -1) + : x_coord(x_coord), y_coord(y_coord), internal_id(internal_id), original_node_id(node_id) {} GridUnit x_coord; GridUnit y_coord; NodeId internal_id; // unique ascending identifier - NodeId node_id; // original node id + NodeId original_node_id; // original node id - bool operator==(Terminal const &other) const; + bool operator==(Node const &other) const; - bool operator<(Terminal const &other) const; + bool operator<(Node const &other) const; - [[nodiscard]] GridUnit distance(Terminal const &other) const; + [[nodiscard]] GridUnit distance(Node const &other) const; }; - explicit DelaunayGraph(std::vector terminals); + DelaunayGraph() : _max_x(0), _max_y(0), _orig_max_x(0), _orig_max_y(0), _num_terminals(0) {} + + explicit DelaunayGraph(std::vector terminals); struct Edge { - Terminal terminal_a; - Terminal terminal_b; + Node node_a; + Node node_b; [[nodiscard]] bool operator<(Edge const &other) const; + [[nodiscard]] bool operator==(Edge const &other) const; }; - void add_terminal(GridUnit x_coord, GridUnit y_coord, NodeId terminal_id = -1); - - void add_edge(Terminal terminal_a, Terminal terminal_b); - - void calculate(); - - [[maybe_unused]] void primitive_print(std::ostream &os); + void add_node(GridUnit x_coord, GridUnit y_coord, NodeId node_id = -1); + void add_terminal(GridUnit x_coord, GridUnit y_coord, NodeId node_id = -1); + void add_edge(Node terminal_a, Node terminal_b); + void calculate_l1_delaunay_triangulation(); void translate_from_1_to_infty_norm(); - void translate_from_infty_to_1_norm(); - Graph export_graph(); + void add_steiner_points(); + Graph export_graph(); [[maybe_unused]] void print_as_postscript(std::ostream &os, const std::string &base_file_name); - [[nodiscard]] NodeId num_terminals() const; + [[nodiscard]] NodeId num_nodes() const; [[nodiscard]] EdgeId num_edges() const; - - [[nodiscard]] std::vector const &terminals() const; - + [[nodiscard]] std::vector const &nodes() const; [[nodiscard]] std::vector const &edges() const; private: - static void update_inactivation_records(DelaunayPriorityQueue &X, DelaunaySet const &Y, Terminal terminal); + static void update_inactivation_records(DelaunayPriorityQueue &X, DelaunaySet const &Y, Node terminal); - static void translate_terminal_from_1_to_infty_norm(Terminal &t); + static void translate_terminal_from_1_to_infty_norm(Node &t); - static void translate_terminal_from_infty_to_1_norm(Terminal &t); + static void translate_terminal_from_infty_to_1_norm(Node &t); - std::vector _terminals; + std::vector _nodes; std::vector _edges; GridUnit _max_x, _max_y, _orig_max_x, _orig_max_y; + + NodeId _num_terminals; }; diff --git a/delaunay/DelaunayPriorityQueue.h b/delaunay/DelaunayPriorityQueue.h index fa3b2e6..ac9b39d 100644 --- a/delaunay/DelaunayPriorityQueue.h +++ b/delaunay/DelaunayPriorityQueue.h @@ -19,7 +19,7 @@ class DelaunayPriorityQueue { DelaunayPriorityQueue(GridUnit max_x, GridUnit max_y) : MINIMUM_RECORD( {{-max_x - 1, -max_y - 1}, -max_x - max_y - 1, INACTIVE}) {} - using Terminal = DelaunayGraph::Terminal; + using Terminal = DelaunayGraph::Node; struct Record { Terminal terminal; GridUnit trans; diff --git a/delaunay/DelaunaySet.h b/delaunay/DelaunaySet.h index 766167f..52a48ee 100644 --- a/delaunay/DelaunaySet.h +++ b/delaunay/DelaunaySet.h @@ -10,7 +10,7 @@ class DelaunaySet { public: - using Terminal = DelaunayGraph::Terminal; + using Terminal = DelaunayGraph::Node; void insert(Terminal const &t); void del(Terminal const &t); diff --git a/dijkstra/DijkstraGraph.cpp b/dijkstra/DijkstraGraph.cpp index 920797d..7ae4f63 100644 --- a/dijkstra/DijkstraGraph.cpp +++ b/dijkstra/DijkstraGraph.cpp @@ -80,7 +80,7 @@ void DijkstraGraph::dijkstras_algorithm(NodeId root_node_id) { if (node_v_id == root_node_id) { node_w.closest_terminal = node_w_id; } - // Otherwise w has the same closest Terminal as v, if not updated later. + // Otherwise w has the same closest Node as v, if not updated later. else { node_w.closest_terminal = node_v.closest_terminal; } @@ -101,7 +101,7 @@ void DijkstraGraph::dijkstras_algorithm(NodeId root_node_id) { if (node_v_id == root_node_id) { node_w.closest_terminal = node_w_id; } - // Otherwise w has the same closest Terminal as v, if not updated later. + // Otherwise w has the same closest Node as v, if not updated later. else { node_w.closest_terminal = node_v.closest_terminal; } diff --git a/kruskal/kruskal_ind.cpp b/kruskal/kruskal_ind.cpp index f476cee..31988dc 100644 --- a/kruskal/kruskal_ind.cpp +++ b/kruskal/kruskal_ind.cpp @@ -34,7 +34,7 @@ Graph kruskal(const Graph &input_graph) { } DelaunayGraph kruskal(DelaunayGraph const &delaunay_graph) { - DelaunayGraph return_graph(delaunay_graph.terminals()); + DelaunayGraph return_graph(delaunay_graph.nodes()); std::vector edge_ids(delaunay_graph.num_edges()); std::iota(edge_ids.begin(), edge_ids.end(), 0); @@ -45,15 +45,15 @@ DelaunayGraph kruskal(DelaunayGraph const &delaunay_graph) { return (id_to_edge_projection(a) < id_to_edge_projection(b)); }); Disjoint_Set set; - set.make_sets(return_graph.num_terminals()); + set.make_sets(return_graph.num_nodes()); for (auto edge_id: edge_ids) { auto const &edge = id_to_edge_projection(edge_id); - if (!set.set_equals(edge.terminal_a.internal_id, edge.terminal_b.internal_id)) { + if (!set.set_equals(edge.node_a.internal_id, edge.node_b.internal_id)) { // unite the sets of the parents of the nodes of edge - set.unite(edge.terminal_a.internal_id, edge.terminal_b.internal_id); + set.unite(edge.node_a.internal_id, edge.node_b.internal_id); // add edge to the vector of included edges - return_graph.add_edge(edge.terminal_a, edge.terminal_b); + return_graph.add_edge(edge.node_a, edge.node_b); } } diff --git a/main.cpp b/main.cpp index 704b9fd..780b83a 100644 --- a/main.cpp +++ b/main.cpp @@ -22,11 +22,12 @@ int main(int argc, char *argv[]) { return 1; } - delaunay_graph.translate_from_1_to_infty_norm(); - delaunay_graph.calculate(); - delaunay_graph.translate_from_infty_to_1_norm(); + delaunay_graph.calculate_l1_delaunay_triangulation(); + delaunay_graph.add_steiner_points(); + delaunay_graph.calculate_l1_delaunay_triangulation(); print_verbose("Delaunay triangulation complete"); + auto kruskal_graph = kruskal(delaunay_graph); print_verbose("first kruskal complete"); From ba60bcb99f5dc432a9de698e2f4b384e5dbf6283 Mon Sep 17 00:00:00 2001 From: JakobGie <44549596+armytrong@users.noreply.github.com> Date: Tue, 5 Jul 2022 01:02:58 +0200 Subject: [PATCH 19/25] created true mehlhorn graph --- CMakeLists.txt | 2 +- MehlhornGraphFactory.cpp | 3 +- delaunay/DelaunayGraph.cpp | 8 +++ delaunay/DelaunayGraph.h | 5 +- dijkstra/DijkstraGraph.cpp | 31 +++++++++--- dijkstra/DijkstraGraph.h | 4 ++ graph/GraphInterface.h | 17 +++++++ mehlhorn/MehlhornGraph.cpp | 99 ++++++++++++++++++++++++++++++++++++++ mehlhorn/MehlhornGraph.h | 44 +++++++++++++++++ 9 files changed, 201 insertions(+), 12 deletions(-) create mode 100644 graph/GraphInterface.h create mode 100644 mehlhorn/MehlhornGraph.cpp create mode 100644 mehlhorn/MehlhornGraph.h diff --git a/CMakeLists.txt b/CMakeLists.txt index c44f57d..4fda237 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,4 +32,4 @@ add_executable(Hauptaufgabe_2 delaunay/DelaunayPriorityQueue.cpp delaunay/DelaunayPriorityQueue.h ArgumentHandler.cpp - ArgumentHandler.h) + ArgumentHandler.h mehlhorn/MehlhornGraph.cpp mehlhorn/MehlhornGraph.h) diff --git a/MehlhornGraphFactory.cpp b/MehlhornGraphFactory.cpp index f2c1b4b..017e4eb 100644 --- a/MehlhornGraphFactory.cpp +++ b/MehlhornGraphFactory.cpp @@ -6,8 +6,7 @@ #include #include "MehlhornGraphFactory.h" -void -MehlhornGraphFactory::bucket_sort( +void MehlhornGraphFactory::bucket_sort( std::vector &vector, MehlhornGraphFactory::TypeToNodeId type_to_node_id, NodeId max_id) { diff --git a/delaunay/DelaunayGraph.cpp b/delaunay/DelaunayGraph.cpp index d119082..d04fbcd 100644 --- a/delaunay/DelaunayGraph.cpp +++ b/delaunay/DelaunayGraph.cpp @@ -235,6 +235,10 @@ void DelaunayGraph::add_steiner_points() { } } +NodeId DelaunayGraph::num_terminals() const { return _num_terminals; } + +bool DelaunayGraph::is_terminal_id(NodeId node_id) const { return node_id < num_terminals(); } + bool DelaunayGraph::Node::operator==(const DelaunayGraph::Node &other) const { return x_coord == other.x_coord and y_coord == other.y_coord; } @@ -254,3 +258,7 @@ bool DelaunayGraph::Edge::operator<(const DelaunayGraph::Edge &other) const { bool DelaunayGraph::Edge::operator==(Edge const &other) const { return node_a == other.node_a and node_b == other.node_b; } + +WeightType DelaunayGraph::Edge::length() const { + return std::abs(node_a.x_coord - node_b.x_coord) + std::abs(node_a.y_coord - node_b.y_coord); +} diff --git a/delaunay/DelaunayGraph.h b/delaunay/DelaunayGraph.h index c9deb7b..8b2265b 100644 --- a/delaunay/DelaunayGraph.h +++ b/delaunay/DelaunayGraph.h @@ -45,6 +45,7 @@ class DelaunayGraph { [[nodiscard]] bool operator<(Edge const &other) const; [[nodiscard]] bool operator==(Edge const &other) const; + [[nodiscard]] WeightType length() const; }; void add_node(GridUnit x_coord, GridUnit y_coord, NodeId node_id = -1); @@ -61,11 +62,13 @@ class DelaunayGraph { [[maybe_unused]] void print_as_postscript(std::ostream &os, const std::string &base_file_name); [[nodiscard]] NodeId num_nodes() const; - + [[nodiscard]] NodeId num_terminals() const; [[nodiscard]] EdgeId num_edges() const; [[nodiscard]] std::vector const &nodes() const; [[nodiscard]] std::vector const &edges() const; + [[nodiscard]] bool is_terminal_id(NodeId node_id) const; + private: diff --git a/dijkstra/DijkstraGraph.cpp b/dijkstra/DijkstraGraph.cpp index 7ae4f63..611e8e0 100644 --- a/dijkstra/DijkstraGraph.cpp +++ b/dijkstra/DijkstraGraph.cpp @@ -8,6 +8,7 @@ #include "../heap/FibonacciHeap.h" #include "../heap/StandardHeap.h" #include +#include DijkstraGraph::DijkstraGraph(Graph const &graph) : _calculation_finished(false) { @@ -25,15 +26,28 @@ DijkstraGraph::DijkstraGraph(Graph const &graph) : _calculation_finished(false) } } +DijkstraGraph::DijkstraGraph(const DelaunayGraph &delaunay_graph) { + _nodes = std::vector(delaunay_graph.num_nodes()); + for (size_t i = 0; i < _nodes.size(); i++) { + _nodes[i]._id = static_cast(i); + } + + for (auto const &edge: delaunay_graph.edges()) { + _nodes[edge.node_a.internal_id].neighbours.push_back(edge.node_b.internal_id); + _nodes[edge.node_a.internal_id].weights.push_back(edge.length()); + + _nodes[edge.node_b.internal_id].neighbours.push_back(edge.node_a.internal_id); + _nodes[edge.node_b.internal_id].weights.push_back(edge.length()); + } +} + const DijkstraGraph::Node &DijkstraGraph::operator[](NodeId index) const { return _nodes[index]; } void DijkstraGraph::dijkstras_algorithm(NodeId root_node_id) { - if (root_node_id >= _nodes.size()) { - throw std::invalid_argument("Root node not in graph."); - } + assert(root_node_id >= _nodes.size() && "Root node must be in graph."); Node &root_node = _nodes[root_node_id]; @@ -43,12 +57,8 @@ void DijkstraGraph::dijkstras_algorithm(NodeId root_node_id) { node.predecessor = -1; } - StandardHeap candidates = StandardHeap{}; + StandardHeap candidates; // FibonacciHeap candidates; - Heap &candidates_2 = candidates; - Heap *candidates_3 = new StandardHeap{}; - - delete candidates_3; candidates.insert(root_node_id, 0); @@ -139,6 +149,11 @@ NodeId DijkstraGraph::add_node(std::vector neighbours, std::vector #include "typedefs.h" #include "graph/Graph.h" +#include "delaunay/DelaunayGraph.h" class DijkstraGraph { public: @@ -24,6 +25,7 @@ class DijkstraGraph { explicit DijkstraGraph(Graph const &graph); + explicit DijkstraGraph(DelaunayGraph const &delaunay_graph); Node &operator[](NodeId index); @@ -37,6 +39,8 @@ class DijkstraGraph { [[nodiscard]] bool calculation_finished() const; + [[nodiscard]] NodeId predecessor(NodeId node) const; + private: std::vector _nodes; bool _calculation_finished; diff --git a/graph/GraphInterface.h b/graph/GraphInterface.h new file mode 100644 index 0000000..6eccfa7 --- /dev/null +++ b/graph/GraphInterface.h @@ -0,0 +1,17 @@ +// +// Created by jgier on 04.07.2022. +// + +#ifndef HAUPTAUFGABE_2_GRAPHINTERFACE_H +#define HAUPTAUFGABE_2_GRAPHINTERFACE_H + + +#include "typedefs.h" + +class GraphInterface { + [[nodiscard]] virtual NodeId num_nodes() const = 0; + [[nodiscard]] virtual NodeId num_edges() const = 0; +}; + + +#endif //HAUPTAUFGABE_2_GRAPHINTERFACE_H diff --git a/mehlhorn/MehlhornGraph.cpp b/mehlhorn/MehlhornGraph.cpp new file mode 100644 index 0000000..87b8679 --- /dev/null +++ b/mehlhorn/MehlhornGraph.cpp @@ -0,0 +1,99 @@ +// +// Created by jgier on 04.07.2022. +// + +#include +#include +#include +#include "MehlhornGraph.h" +#include "dijkstra/DijkstraGraph.h" + +void MehlhornGraph::calculate_mehlhorn_graph() { + DijkstraGraph dijkstra_graph(_delaunay_graph); + std::vector terminals(_delaunay_graph.num_terminals()); + std::iota(terminals.begin(), terminals.end(), 0); + NodeId root_node = dijkstra_graph.add_node(terminals, std::vector(terminals.size(), 0)); + dijkstra_graph.dijkstras_algorithm(root_node); + + std::vector candidate_mehlhorn_edges; + for (auto const &edge: _delaunay_graph.edges()) { + NodeId terminal_1 = dijkstra_graph[edge.node_a.internal_id].closest_terminal; + NodeId terminal_2 = dijkstra_graph[edge.node_b.internal_id].closest_terminal; + WeightType length = dijkstra_graph[edge.node_a.internal_id].distance_to_root + edge.length() + + dijkstra_graph[edge.node_b.internal_id].distance_to_root; + assert(terminal_1 != terminal_2); + candidate_mehlhorn_edges.push_back({terminal_1, terminal_2, length}); + auto &candidate_edge = candidate_mehlhorn_edges.back(); + + add_path_to_edge(dijkstra_graph, edge, candidate_edge); + + } + + bucket_sort( + candidate_mehlhorn_edges, + [](Edge const &edge) { return edge.smaller_node(); }, + _delaunay_graph.num_nodes() + ); + + bucket_sort( + candidate_mehlhorn_edges, + [](Edge const &edge) { return edge.larger_node(); }, + _delaunay_graph.num_nodes() + ); + + std::optional current_mincost_edge = std::nullopt; + + for (auto const &candidate_edge: candidate_mehlhorn_edges) { + if (not current_mincost_edge.has_value() + or current_mincost_edge->length > candidate_edge.length) { + current_mincost_edge = candidate_edge; + } else if (not( + current_mincost_edge->larger_node() == candidate_edge.larger_node() and + current_mincost_edge->smaller_node() == candidate_edge.smaller_node() + )) { + _mehlhorn_edges.push_back(*current_mincost_edge); + current_mincost_edge = candidate_edge; + } + } + _mehlhorn_edges.push_back(*current_mincost_edge); +} + +void MehlhornGraph::add_path_to_edge( + DijkstraGraph const &dijkstra_graph, + DelaunayGraph::Edge const &edge, + MehlhornGraph::Edge &candidate_edge) const { + std::vector &path_to_node_a = candidate_edge.path; + NodeId node_i = edge.node_a.internal_id; + while (not _delaunay_graph.is_terminal_id(node_i)) { + path_to_node_a.push_back(node_i); + node_i = dijkstra_graph.predecessor(node_i); + } + path_to_node_a.push_back(node_i); + std::reverse(path_to_node_a.begin(), path_to_node_a.end()); + + std::vector path_to_node_b; + node_i = edge.node_b.internal_id; + while (not _delaunay_graph.is_terminal_id(node_i)) { + path_to_node_a.push_back(node_i); + node_i = dijkstra_graph.predecessor(node_i); + } + path_to_node_a.push_back(node_i); +} + +void MehlhornGraph::bucket_sort( + std::vector &vector, + TypeToNodeId type_to_node_id, + NodeId max_id) { + std::vector> bucket_list; + bucket_list.resize(max_id); + for (auto const &t: vector) { + bucket_list[type_to_node_id(t)].emplace_back(t); + } + size_t orig_i = 0; + for (auto const &bucket: bucket_list) { + for (auto const &item: bucket) { + vector[orig_i] = item; + orig_i++; + } + } +} diff --git a/mehlhorn/MehlhornGraph.h b/mehlhorn/MehlhornGraph.h new file mode 100644 index 0000000..4df24f4 --- /dev/null +++ b/mehlhorn/MehlhornGraph.h @@ -0,0 +1,44 @@ +// +// Created by jgier on 04.07.2022. +// + +#ifndef HAUPTAUFGABE_2_MEHLHORNGRAPH_H +#define HAUPTAUFGABE_2_MEHLHORNGRAPH_H + + +#include "delaunay/DelaunayGraph.h" +#include "dijkstra/DijkstraGraph.h" + +class MehlhornGraph { +public: + struct Edge { + NodeId tail; + NodeId head; + WeightType length; + std::vector path; + + [[nodiscard]] NodeId smaller_node() const { return tail < head ? tail : head; } + + [[nodiscard]] NodeId larger_node() const { return tail > head ? tail : head; } + }; + + explicit MehlhornGraph(DelaunayGraph const &delaunay_graph) : _delaunay_graph(delaunay_graph) {} + + void calculate_mehlhorn_graph(); + +private: + + template + using TypeToNodeId = NodeId (*)(T const &element); + + static void + bucket_sort(std::vector &vector, TypeToNodeId type_to_node_id, NodeId max_id); + + DelaunayGraph const &_delaunay_graph; + std::vector _mehlhorn_edges; + void + add_path_to_edge(const DijkstraGraph &dijkstra_graph, const DelaunayGraph::Edge &edge, Edge &candidate_edge) const; +}; + + +#endif //HAUPTAUFGABE_2_MEHLHORNGRAPH_H From 84906cc807b2f0cabb8c6520759e5527189a4b03 Mon Sep 17 00:00:00 2001 From: JakobGie <44549596+armytrong@users.noreply.github.com> Date: Tue, 5 Jul 2022 01:14:54 +0200 Subject: [PATCH 20/25] implemented kruskal on mehlhorn edges --- kruskal/kruskal_ind.cpp | 8 ++++---- main.cpp | 8 ++++++-- mehlhorn/MehlhornGraph.cpp | 30 ++++++++++++++++++++++++++++++ mehlhorn/MehlhornGraph.h | 4 ++++ 4 files changed, 44 insertions(+), 6 deletions(-) diff --git a/kruskal/kruskal_ind.cpp b/kruskal/kruskal_ind.cpp index 31988dc..cade63c 100644 --- a/kruskal/kruskal_ind.cpp +++ b/kruskal/kruskal_ind.cpp @@ -17,14 +17,14 @@ Graph kruskal(const Graph &input_graph) { [&id_to_edge_projection](EdgeId a, EdgeId b) { return (id_to_edge_projection(a) < id_to_edge_projection(b)); }); - Disjoint_Set set; - set.make_sets(return_graph.num_nodes()); + Disjoint_Set disjoint_set; + disjoint_set.make_sets(return_graph.num_nodes()); for (auto edge_id: edge_ids) { auto const &edge = id_to_edge_projection(edge_id); - if (!set.set_equals(edge._head, edge._tail)) { + if (!disjoint_set.set_equals(edge._head, edge._tail)) { // unite the sets of the parents of the nodes of edge - set.unite(edge._head, edge._tail); + disjoint_set.unite(edge._head, edge._tail); // add edge to the vector of included edges return_graph.add_edge(edge); } diff --git a/main.cpp b/main.cpp index 780b83a..a54126a 100644 --- a/main.cpp +++ b/main.cpp @@ -3,6 +3,7 @@ #include "kruskal/kruskal_ind.h" #include "graph/RectilinearGraph.h" #include "ArgumentHandler.h" +#include "mehlhorn/MehlhornGraph.h" int main(int argc, char *argv[]) { ArgumentHandler argument_handler(argc, argv); @@ -25,10 +26,13 @@ int main(int argc, char *argv[]) { delaunay_graph.calculate_l1_delaunay_triangulation(); delaunay_graph.add_steiner_points(); delaunay_graph.calculate_l1_delaunay_triangulation(); - print_verbose("Delaunay triangulation complete"); + print_verbose("Delaunay triangulation complete."); + MehlhornGraph mehlhorn_graph(delaunay_graph); + mehlhorn_graph.calculate_mehlhorn_graph(); + print_verbose("Mehlhorn Graph calculated."); - auto kruskal_graph = kruskal(delaunay_graph); + mehlhorn_graph.kruskal_on_mehlhorn_edges(); print_verbose("first kruskal complete"); RectilinearGraph rect_graph(kruskal_graph.export_graph()); diff --git a/mehlhorn/MehlhornGraph.cpp b/mehlhorn/MehlhornGraph.cpp index 87b8679..01f3bc5 100644 --- a/mehlhorn/MehlhornGraph.cpp +++ b/mehlhorn/MehlhornGraph.cpp @@ -7,6 +7,7 @@ #include #include "MehlhornGraph.h" #include "dijkstra/DijkstraGraph.h" +#include "kruskal/disjoint_set_2.h" void MehlhornGraph::calculate_mehlhorn_graph() { DijkstraGraph dijkstra_graph(_delaunay_graph); @@ -97,3 +98,32 @@ void MehlhornGraph::bucket_sort( } } } + +void MehlhornGraph::kruskal_on_mehlhorn_edges() { + std::vector edge_ids(_mehlhorn_edges.size()); + std::iota(edge_ids.begin(), edge_ids.end(), 0); + + std::vector included_edges; + + auto id_to_edge_projection = [this](auto const edge_id) { return _mehlhorn_edges[edge_id]; }; + + std::sort(edge_ids.begin(), edge_ids.end(), + [&id_to_edge_projection](EdgeId a, EdgeId b) { + return (id_to_edge_projection(a) < id_to_edge_projection(b)); + }); + Disjoint_Set disjoint_set; + disjoint_set.make_sets(_delaunay_graph.num_nodes()); + for (auto edge_id: edge_ids) { + auto const &edge = id_to_edge_projection(edge_id); + + if (!disjoint_set.set_equals(edge.head, edge.tail)) { + // unite the sets of the parents of the nodes of edge + disjoint_set.unite(edge.head, edge.tail); + // add edge to the vector of included edges + included_edges.push_back(edge); + } + } + _mehlhorn_edges = included_edges; +} + +bool MehlhornGraph::Edge::operator<(const MehlhornGraph::Edge &other) const { return length < other.length; } diff --git a/mehlhorn/MehlhornGraph.h b/mehlhorn/MehlhornGraph.h index 4df24f4..a0b07c4 100644 --- a/mehlhorn/MehlhornGraph.h +++ b/mehlhorn/MehlhornGraph.h @@ -20,12 +20,16 @@ class MehlhornGraph { [[nodiscard]] NodeId smaller_node() const { return tail < head ? tail : head; } [[nodiscard]] NodeId larger_node() const { return tail > head ? tail : head; } + + [[nodiscard]] bool operator<(Edge const &other) const; }; explicit MehlhornGraph(DelaunayGraph const &delaunay_graph) : _delaunay_graph(delaunay_graph) {} void calculate_mehlhorn_graph(); + void kruskal_on_mehlhorn_edges(); + private: template From 5cb46ff1ca2ea1252ffd794c239798b7fa3d80a7 Mon Sep 17 00:00:00 2001 From: JakobGie <44549596+armytrong@users.noreply.github.com> Date: Tue, 5 Jul 2022 02:53:09 +0200 Subject: [PATCH 21/25] added coordinate graph --- CMakeLists.txt | 2 +- STPFileParser.cpp | 4 +- delaunay/DelaunayGraph.cpp | 100 +--------------------------------- delaunay/DelaunayGraph.h | 56 ++++--------------- dijkstra/DijkstraGraph.cpp | 8 +-- dijkstra/DijkstraGraph.h | 2 +- graph/CoordinateGraph.cpp | 108 +++++++++++++++++++++++++++++++++++++ graph/CoordinateGraph.h | 67 +++++++++++++++++++++++ kruskal/kruskal_ind.cpp | 2 +- main.cpp | 51 ++++++++++-------- mehlhorn/MehlhornGraph.cpp | 43 ++++++++++----- mehlhorn/MehlhornGraph.h | 6 ++- 12 files changed, 256 insertions(+), 193 deletions(-) create mode 100644 graph/CoordinateGraph.cpp create mode 100644 graph/CoordinateGraph.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 4fda237..b9a1e89 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,4 +32,4 @@ add_executable(Hauptaufgabe_2 delaunay/DelaunayPriorityQueue.cpp delaunay/DelaunayPriorityQueue.h ArgumentHandler.cpp - ArgumentHandler.h mehlhorn/MehlhornGraph.cpp mehlhorn/MehlhornGraph.h) + ArgumentHandler.h mehlhorn/MehlhornGraph.cpp mehlhorn/MehlhornGraph.h graph/CoordinateGraph.cpp graph/CoordinateGraph.h) diff --git a/STPFileParser.cpp b/STPFileParser.cpp index 0ef7a77..628a799 100644 --- a/STPFileParser.cpp +++ b/STPFileParser.cpp @@ -185,9 +185,9 @@ DelaunayGraph STPFileParser::create_delaunay_graph() { } for (auto terminal_id: _terminals) { if (_node_coords.empty()) { - delaunay_graph.add_node(terminal_id % sqrt, static_cast(terminal_id / sqrt)); + delaunay_graph.add_terminal(terminal_id % sqrt, static_cast(terminal_id / sqrt)); } else { - delaunay_graph.add_node(_node_coords[terminal_id].x, _node_coords[terminal_id].y); + delaunay_graph.add_terminal(_node_coords[terminal_id].x, _node_coords[terminal_id].y); } } return delaunay_graph; diff --git a/delaunay/DelaunayGraph.cpp b/delaunay/DelaunayGraph.cpp index d04fbcd..8ea3d53 100644 --- a/delaunay/DelaunayGraph.cpp +++ b/delaunay/DelaunayGraph.cpp @@ -10,25 +10,10 @@ #include #include -void DelaunayGraph::add_node(GridUnit x_coord, GridUnit y_coord, NodeId node_id) { - _nodes.emplace_back(x_coord, y_coord, _nodes.size(), node_id); - _max_x = std::max(_max_x, std::abs(x_coord)); - _max_y = std::max(_max_y, std::abs(y_coord)); - _orig_max_x = _max_x; - _orig_max_y = _max_y; -} - -void DelaunayGraph::add_terminal(GridUnit x_coord, GridUnit y_coord, NodeId node_id) { - if (_nodes.size() == _num_terminals) { - _nodes.emplace_back(x_coord, y_coord, _nodes.size(), node_id); - } else { - _nodes.emplace(_nodes.begin() + _num_terminals, x_coord, y_coord, _nodes.size(), node_id); - } - _num_terminals += 1; -} void DelaunayGraph::calculate_l1_delaunay_triangulation() { translate_from_1_to_infty_norm(); + _edges.clear(); DelaunayPriorityQueue X(_max_x, _max_y); for (auto const &terminal: _nodes) { @@ -83,24 +68,6 @@ void DelaunayGraph::update_inactivation_records(DelaunayPriorityQueue &X, const } } -Graph DelaunayGraph::export_graph() { - NodeId sqrt = std::max(_orig_max_x, _orig_max_y) + 1; - NodeId num_nodes = sqrt * sqrt; - std::vector terminals; - for (auto const &terminal: _nodes) { - terminals.push_back(terminal.x_coord + terminal.y_coord * sqrt); - } - Graph ret{num_nodes, terminals}; - for (auto const &edge: _edges) { - ret.add_edge({ - edge.node_a.x_coord + edge.node_a.y_coord * sqrt, - edge.node_b.x_coord + edge.node_b.y_coord * sqrt, - edge.node_a.distance(edge.node_b) - }); - } - - return ret; -} void DelaunayGraph::translate_terminal_from_1_to_infty_norm(DelaunayGraph::Node &t) { GridUnit new_x = t.x_coord + t.y_coord; @@ -138,68 +105,6 @@ void DelaunayGraph::translate_from_infty_to_1_norm() { } } -[[maybe_unused]] void DelaunayGraph::print_as_postscript(std::ostream &os, const std::string &base_file_name) { - std::ifstream base_file(base_file_name); - assert(base_file); - std::string line; - - os << base_file.rdbuf(); - base_file.close(); - - auto grid_width = static_cast(std::sqrt(std::max(_orig_max_x, _orig_max_y))); - - os << std::endl; - os << "%%BeginSetup" << std::endl << std::endl; - os << 0 << " " << (grid_width + 1) * (grid_width + 1) << " " << 0 << " " << (grid_width + 1) * (grid_width + 1) - << " SetAxes" - << - std::endl << - std::endl; - - os << _nodes.size() << " DefineTerminals" << std::endl; - for (auto terminal: _nodes) { - os << "\t" << terminal.x_coord + 1 << "\t" << terminal.y_coord + 1 << "\tDT" << std::endl; - } - - os << std::endl << "%%EndSetup" << std::endl << std::endl; - - os << "%%Page: 1 1" << std::endl; - os << "BeginPlot" << std::endl; - os << "\tPlot_Terminals" << std::endl; - for (auto const &edge: _edges) { - os << "\t" << edge.node_a.x_coord + 1 - << "\t" << edge.node_a.y_coord + 1 - << "\t" << edge.node_b.x_coord + 1 - << "\t" << edge.node_b.y_coord + 1 << "\tS" << std::endl; - } - - //os << " (Steiner Minimal Tree: " << _terminals.size() << "points, length=" << grid_width * grid_widht << ")" << - //std::endl; - os << "EndPlot" << std::endl; -} - -const std::vector &DelaunayGraph::nodes() const { return _nodes; } - -EdgeId DelaunayGraph::num_edges() const { return static_cast(_edges.size()); } - -const std::vector &DelaunayGraph::edges() const { return _edges; } - -NodeId DelaunayGraph::num_nodes() const { return static_cast(_nodes.size()); } - -void DelaunayGraph::add_edge(DelaunayGraph::Node terminal_a, DelaunayGraph::Node terminal_b) { - _edges.push_back({terminal_a, terminal_b}); -} - -DelaunayGraph::DelaunayGraph(std::vector terminals) : - _max_x(0), _max_y(0), _orig_max_x(0), _orig_max_y(0), _nodes(std::move(terminals)) { - for (auto const &terminal: _nodes) { - _max_x = std::max(std::abs(terminal.x_coord), _max_x); - _max_y = std::max(std::abs(terminal.y_coord), _max_y); - _orig_max_x = _max_x; - _orig_max_y = _max_y; - } - _num_terminals = static_cast(_nodes.size()); -} void DelaunayGraph::add_steiner_points() { std::vector> edges_by_node_id(num_nodes()); @@ -235,9 +140,6 @@ void DelaunayGraph::add_steiner_points() { } } -NodeId DelaunayGraph::num_terminals() const { return _num_terminals; } - -bool DelaunayGraph::is_terminal_id(NodeId node_id) const { return node_id < num_terminals(); } bool DelaunayGraph::Node::operator==(const DelaunayGraph::Node &other) const { return x_coord == other.x_coord and y_coord == other.y_coord; diff --git a/delaunay/DelaunayGraph.h b/delaunay/DelaunayGraph.h index 8b2265b..eaa8585 100644 --- a/delaunay/DelaunayGraph.h +++ b/delaunay/DelaunayGraph.h @@ -8,8 +8,9 @@ #include #include -#include "../typedefs.h" -#include "../graph/Graph.h" +#include "typedefs.h" +#include "graph/Graph.h" +#include "graph/CoordinateGraph.h" class DelaunayPriorityQueue; @@ -17,40 +18,16 @@ class DelaunayPriorityQueue; class DelaunaySet; -class DelaunayGraph { +class DelaunayGraph : public CoordinateGraph { public: - struct Node { - Node(GridUnit x_coord, GridUnit y_coord, NodeId internal_id = -1, NodeId node_id = -1) - : x_coord(x_coord), y_coord(y_coord), internal_id(internal_id), original_node_id(node_id) {} + using super = CoordinateGraph; + using Node = super::Node; + using Edge = super::Edge; - GridUnit x_coord; - GridUnit y_coord; - NodeId internal_id; // unique ascending identifier - NodeId original_node_id; // original node id + DelaunayGraph() : super() {} - bool operator==(Node const &other) const; + explicit DelaunayGraph(std::vector nodes, NodeId num_terminals) : super(std::move(nodes), num_terminals) {} - bool operator<(Node const &other) const; - - [[nodiscard]] GridUnit distance(Node const &other) const; - }; - - DelaunayGraph() : _max_x(0), _max_y(0), _orig_max_x(0), _orig_max_y(0), _num_terminals(0) {} - - explicit DelaunayGraph(std::vector terminals); - - struct Edge { - Node node_a; - Node node_b; - - [[nodiscard]] bool operator<(Edge const &other) const; - [[nodiscard]] bool operator==(Edge const &other) const; - [[nodiscard]] WeightType length() const; - }; - - void add_node(GridUnit x_coord, GridUnit y_coord, NodeId node_id = -1); - void add_terminal(GridUnit x_coord, GridUnit y_coord, NodeId node_id = -1); - void add_edge(Node terminal_a, Node terminal_b); void calculate_l1_delaunay_triangulation(); void translate_from_1_to_infty_norm(); @@ -58,17 +35,6 @@ class DelaunayGraph { void add_steiner_points(); - Graph export_graph(); - [[maybe_unused]] void print_as_postscript(std::ostream &os, const std::string &base_file_name); - - [[nodiscard]] NodeId num_nodes() const; - [[nodiscard]] NodeId num_terminals() const; - [[nodiscard]] EdgeId num_edges() const; - [[nodiscard]] std::vector const &nodes() const; - [[nodiscard]] std::vector const &edges() const; - - [[nodiscard]] bool is_terminal_id(NodeId node_id) const; - private: @@ -78,11 +44,7 @@ class DelaunayGraph { static void translate_terminal_from_infty_to_1_norm(Node &t); - std::vector _nodes; - std::vector _edges; - GridUnit _max_x, _max_y, _orig_max_x, _orig_max_y; - NodeId _num_terminals; }; diff --git a/dijkstra/DijkstraGraph.cpp b/dijkstra/DijkstraGraph.cpp index 611e8e0..1ea9eef 100644 --- a/dijkstra/DijkstraGraph.cpp +++ b/dijkstra/DijkstraGraph.cpp @@ -26,13 +26,13 @@ DijkstraGraph::DijkstraGraph(Graph const &graph) : _calculation_finished(false) } } -DijkstraGraph::DijkstraGraph(const DelaunayGraph &delaunay_graph) { - _nodes = std::vector(delaunay_graph.num_nodes()); +DijkstraGraph::DijkstraGraph(const CoordinateGraph &coordinate_graph) : _calculation_finished(false) { + _nodes = std::vector(coordinate_graph.num_nodes()); for (size_t i = 0; i < _nodes.size(); i++) { _nodes[i]._id = static_cast(i); } - for (auto const &edge: delaunay_graph.edges()) { + for (auto const &edge: coordinate_graph.edges()) { _nodes[edge.node_a.internal_id].neighbours.push_back(edge.node_b.internal_id); _nodes[edge.node_a.internal_id].weights.push_back(edge.length()); @@ -47,7 +47,7 @@ const DijkstraGraph::Node &DijkstraGraph::operator[](NodeId index) const { void DijkstraGraph::dijkstras_algorithm(NodeId root_node_id) { - assert(root_node_id >= _nodes.size() && "Root node must be in graph."); + assert(root_node_id < _nodes.size() && "Root node must be in graph."); Node &root_node = _nodes[root_node_id]; diff --git a/dijkstra/DijkstraGraph.h b/dijkstra/DijkstraGraph.h index 27a9d22..e495bd1 100644 --- a/dijkstra/DijkstraGraph.h +++ b/dijkstra/DijkstraGraph.h @@ -25,7 +25,7 @@ class DijkstraGraph { explicit DijkstraGraph(Graph const &graph); - explicit DijkstraGraph(DelaunayGraph const &delaunay_graph); + explicit DijkstraGraph(CoordinateGraph const &coordinate_graph); Node &operator[](NodeId index); diff --git a/graph/CoordinateGraph.cpp b/graph/CoordinateGraph.cpp new file mode 100644 index 0000000..b5f5e6f --- /dev/null +++ b/graph/CoordinateGraph.cpp @@ -0,0 +1,108 @@ +// +// Created by jgier on 05.07.2022. +// + +#include "CoordinateGraph.h" +#include +#include +#include + +CoordinateGraph::CoordinateGraph(std::vector nodes, NodeId num_terminals) : + _max_x(0), _max_y(0), _orig_max_x(0), _orig_max_y(0), _nodes(std::move(nodes)), _num_terminals(num_terminals) { + for (auto const &terminal: _nodes) { + _max_x = std::max(std::abs(terminal.x_coord), _max_x); + _max_y = std::max(std::abs(terminal.y_coord), _max_y); + _orig_max_x = _max_x; + _orig_max_y = _max_y; + } +} + +Graph CoordinateGraph::export_graph() const { + NodeId sqrt = std::max(_orig_max_x, _orig_max_y) + 1; + NodeId num_nodes = sqrt * sqrt; + std::vector terminals; + for (auto const &terminal: _nodes) { + terminals.push_back(terminal.x_coord + terminal.y_coord * sqrt); + } + Graph ret{num_nodes, terminals}; + for (auto const &edge: _edges) { + ret.add_edge({ + edge.node_a.x_coord + edge.node_a.y_coord * sqrt, + edge.node_b.x_coord + edge.node_b.y_coord * sqrt, + edge.node_a.distance(edge.node_b) + }); + } + return ret; +} + +void CoordinateGraph::print_as_postscript(std::ostream &os, const std::string &base_file_name) { + std::ifstream base_file(base_file_name); + assert(base_file); + std::string line; + + os << base_file.rdbuf(); + base_file.close(); + + auto grid_width = static_cast(std::sqrt(std::max(_orig_max_x, _orig_max_y))); + + os << std::endl; + os << "%%BeginSetup" << std::endl << std::endl; + os << 0 << " " << (grid_width + 1) * (grid_width + 1) << " " << 0 << " " << (grid_width + 1) * (grid_width + 1) + << " SetAxes" << std::endl << std::endl; + + os << num_terminals() << " DefineTerminals" << std::endl; + for (auto terminal: _nodes) { + if (is_terminal_id(terminal.internal_id)) { + os << "\t" << terminal.x_coord + 1 << "\t" << terminal.y_coord + 1 << "\tDT" << std::endl; + } + } + + os << std::endl << "%%EndSetup" << std::endl << std::endl; + + os << "%%Page: 1 1" << std::endl; + os << "BeginPlot" << std::endl; + os << "\tPlot_Terminals" << std::endl; + for (auto const &edge: _edges) { + os << "\t" << edge.node_a.x_coord + 1 + << "\t" << edge.node_a.y_coord + 1 + << "\t" << edge.node_b.x_coord + 1 + << "\t" << edge.node_b.y_coord + 1 << "\tS" << std::endl; + } + + //os << " (Steiner Minimal Tree: " << _terminals.size() << "points, length=" << grid_width * grid_widht << ")" << + //std::endl; + os << "EndPlot" << std::endl; +} + +const std::vector &CoordinateGraph::nodes() const { return _nodes; } + +EdgeId CoordinateGraph::num_edges() const { return static_cast(_edges.size()); } + +const std::vector &CoordinateGraph::edges() const { return _edges; } + +NodeId CoordinateGraph::num_nodes() const { return static_cast(_nodes.size()); } + +NodeId CoordinateGraph::num_terminals() const { return _num_terminals; } + +bool CoordinateGraph::is_terminal_id(NodeId node_id) const { return node_id < num_terminals(); } + +void CoordinateGraph::add_edge(CoordinateGraph::Node node_a, CoordinateGraph::Node node_b) { + _edges.push_back({node_a, node_b}); +} + +void CoordinateGraph::add_node(GridUnit x_coord, GridUnit y_coord) { + _nodes.emplace_back(x_coord, y_coord, _nodes.size()); + _max_x = std::max(_max_x, std::abs(x_coord)); + _max_y = std::max(_max_y, std::abs(y_coord)); + _orig_max_x = _max_x; + _orig_max_y = _max_y; +} + +void CoordinateGraph::add_terminal(GridUnit x_coord, GridUnit y_coord) { + if (_nodes.size() == _num_terminals) { + _nodes.emplace_back(x_coord, y_coord, _nodes.size()); + } else { + _nodes.emplace(_nodes.begin() + _num_terminals, x_coord, y_coord, _nodes.size()); + } + _num_terminals += 1; +} \ No newline at end of file diff --git a/graph/CoordinateGraph.h b/graph/CoordinateGraph.h new file mode 100644 index 0000000..8525e27 --- /dev/null +++ b/graph/CoordinateGraph.h @@ -0,0 +1,67 @@ +// +// Created by jgier on 05.07.2022. +// + +#ifndef HAUPTAUFGABE_2_COORDINATEGRAPH_H +#define HAUPTAUFGABE_2_COORDINATEGRAPH_H + + +#include "typedefs.h" +#include "Graph.h" + +class CoordinateGraph { +public: + struct Node { + Node(GridUnit x_coord, GridUnit y_coord, NodeId internal_id = -1) + : x_coord(x_coord), y_coord(y_coord), internal_id(internal_id) {} + + GridUnit x_coord; + GridUnit y_coord; + NodeId internal_id; // unique ascending identifier + + bool operator==(Node const &other) const; + + bool operator<(Node const &other) const; + + [[nodiscard]] GridUnit distance(Node const &other) const; + }; + + CoordinateGraph() : _max_x(0), _max_y(0), _orig_max_x(0), _orig_max_y(0), _num_terminals(0) {} + + explicit CoordinateGraph(std::vector nodes, NodeId num_terminals); + + struct Edge { + Node node_a; + Node node_b; + + [[nodiscard]] bool operator<(Edge const &other) const; + [[nodiscard]] bool operator==(Edge const &other) const; + [[nodiscard]] WeightType length() const; + }; + + [[maybe_unused]] [[nodiscard]] Graph export_graph() const; + [[maybe_unused]] void print_as_postscript(std::ostream &os, const std::string &base_file_name); + + [[nodiscard]] NodeId num_nodes() const; + [[nodiscard]] NodeId num_terminals() const; + [[nodiscard]] EdgeId num_edges() const; + [[nodiscard]] std::vector const &nodes() const; + [[nodiscard]] std::vector const &edges() const; + + [[nodiscard]] bool is_terminal_id(NodeId node_id) const; + + void add_node(GridUnit x_coord, GridUnit y_coord); + void add_terminal(GridUnit x_coord, GridUnit y_coord); + void add_edge(Node node_a, Node node_b); + +protected: + std::vector _nodes; + std::vector _edges; + GridUnit _max_x, _max_y, _orig_max_x, _orig_max_y; + + NodeId _num_terminals; + +}; + + +#endif //HAUPTAUFGABE_2_COORDINATEGRAPH_H diff --git a/kruskal/kruskal_ind.cpp b/kruskal/kruskal_ind.cpp index cade63c..8fd16e8 100644 --- a/kruskal/kruskal_ind.cpp +++ b/kruskal/kruskal_ind.cpp @@ -34,7 +34,7 @@ Graph kruskal(const Graph &input_graph) { } DelaunayGraph kruskal(DelaunayGraph const &delaunay_graph) { - DelaunayGraph return_graph(delaunay_graph.nodes()); + DelaunayGraph return_graph(delaunay_graph.nodes(), delaunay_graph.num_terminals()); std::vector edge_ids(delaunay_graph.num_edges()); std::iota(edge_ids.begin(), edge_ids.end(), 0); diff --git a/main.cpp b/main.cpp index a54126a..25527fa 100644 --- a/main.cpp +++ b/main.cpp @@ -28,35 +28,42 @@ int main(int argc, char *argv[]) { delaunay_graph.calculate_l1_delaunay_triangulation(); print_verbose("Delaunay triangulation complete."); + MehlhornGraph mehlhorn_graph(delaunay_graph); mehlhorn_graph.calculate_mehlhorn_graph(); print_verbose("Mehlhorn Graph calculated."); - +// std::ofstream file("Output/output.ps"); +// delaunay_graph.print_as_postscript(file, "PostScript/template.ps"); +// file.close(); mehlhorn_graph.kruskal_on_mehlhorn_edges(); print_verbose("first kruskal complete"); - RectilinearGraph rect_graph(kruskal_graph.export_graph()); - print_verbose("constructed rectiliear graph"); - - auto graph2 = rect_graph.export_graph(); - auto kruskal_graph2 = kruskal(graph2); - print_verbose("second kruskal complete"); - - std::cout << std::endl; - - std::ofstream file(parms.output_filename); - std::ostream &ostream = (parms.output_filename.empty() or not file) ? std::cout : file; - if (not file and not parms.output_filename.empty()) { - std::cerr << "unable to open output file, falling back to standard output" << std::endl; - } - - if (parms.output_format == OutputFormat::STP) { - kruskal_graph2.print_graph(ostream); - } else if (parms.output_format == OutputFormat::PS) { - RectilinearGraph output_graph(kruskal_graph2); - output_graph.print_as_postscript(ostream, "PostScript/template.ps"); - } + auto coordinate_graph = mehlhorn_graph.reconstruct_coord_graph_from_mehlhorn_edges(); + std::ofstream file("Output/output.ps"); + coordinate_graph.print_as_postscript(file, "PostScript/template.ps"); file.close(); +// RectilinearGraph rect_graph(kruskal_graph.export_graph()); +// print_verbose("constructed rectiliear graph"); +// +// auto graph2 = rect_graph.export_graph(); +// auto kruskal_graph2 = kruskal(graph2); +// print_verbose("second kruskal complete"); +// +// std::cout << std::endl; +// +// std::ofstream file(parms.output_filename); +// std::ostream &ostream = (parms.output_filename.empty() or not file) ? std::cout : file; +// if (not file and not parms.output_filename.empty()) { +// std::cerr << "unable to open output file, falling back to standard output" << std::endl; +// } +// +// if (parms.output_format == OutputFormat::STP) { +// kruskal_graph2.print_graph(ostream); +// } else if (parms.output_format == OutputFormat::PS) { +// RectilinearGraph output_graph(kruskal_graph2); +// output_graph.print_as_postscript(ostream, "PostScript/template.ps"); +// } +// file.close(); return 0; diff --git a/mehlhorn/MehlhornGraph.cpp b/mehlhorn/MehlhornGraph.cpp index 01f3bc5..27582d5 100644 --- a/mehlhorn/MehlhornGraph.cpp +++ b/mehlhorn/MehlhornGraph.cpp @@ -10,43 +10,43 @@ #include "kruskal/disjoint_set_2.h" void MehlhornGraph::calculate_mehlhorn_graph() { - DijkstraGraph dijkstra_graph(_delaunay_graph); - std::vector terminals(_delaunay_graph.num_terminals()); + DijkstraGraph dijkstra_graph(_coordinate_graph); + std::vector terminals(_coordinate_graph.num_terminals()); std::iota(terminals.begin(), terminals.end(), 0); NodeId root_node = dijkstra_graph.add_node(terminals, std::vector(terminals.size(), 0)); dijkstra_graph.dijkstras_algorithm(root_node); std::vector candidate_mehlhorn_edges; - for (auto const &edge: _delaunay_graph.edges()) { + for (auto const &edge: _coordinate_graph.edges()) { NodeId terminal_1 = dijkstra_graph[edge.node_a.internal_id].closest_terminal; NodeId terminal_2 = dijkstra_graph[edge.node_b.internal_id].closest_terminal; WeightType length = dijkstra_graph[edge.node_a.internal_id].distance_to_root + edge.length() + dijkstra_graph[edge.node_b.internal_id].distance_to_root; - assert(terminal_1 != terminal_2); - candidate_mehlhorn_edges.push_back({terminal_1, terminal_2, length}); - auto &candidate_edge = candidate_mehlhorn_edges.back(); + if (terminal_1 != terminal_2) { + candidate_mehlhorn_edges.push_back({terminal_1, terminal_2, length}); + auto &candidate_edge = candidate_mehlhorn_edges.back(); - add_path_to_edge(dijkstra_graph, edge, candidate_edge); + add_path_to_edge(dijkstra_graph, edge, candidate_edge); + } } bucket_sort( candidate_mehlhorn_edges, [](Edge const &edge) { return edge.smaller_node(); }, - _delaunay_graph.num_nodes() + _coordinate_graph.num_nodes() ); bucket_sort( candidate_mehlhorn_edges, [](Edge const &edge) { return edge.larger_node(); }, - _delaunay_graph.num_nodes() + _coordinate_graph.num_nodes() ); std::optional current_mincost_edge = std::nullopt; for (auto const &candidate_edge: candidate_mehlhorn_edges) { - if (not current_mincost_edge.has_value() - or current_mincost_edge->length > candidate_edge.length) { + if (not current_mincost_edge.has_value()) { current_mincost_edge = candidate_edge; } else if (not( current_mincost_edge->larger_node() == candidate_edge.larger_node() and @@ -54,6 +54,8 @@ void MehlhornGraph::calculate_mehlhorn_graph() { )) { _mehlhorn_edges.push_back(*current_mincost_edge); current_mincost_edge = candidate_edge; + } else if (current_mincost_edge->length > candidate_edge.length) { + current_mincost_edge = candidate_edge; } } _mehlhorn_edges.push_back(*current_mincost_edge); @@ -65,7 +67,7 @@ void MehlhornGraph::add_path_to_edge( MehlhornGraph::Edge &candidate_edge) const { std::vector &path_to_node_a = candidate_edge.path; NodeId node_i = edge.node_a.internal_id; - while (not _delaunay_graph.is_terminal_id(node_i)) { + while (not _coordinate_graph.is_terminal_id(node_i)) { path_to_node_a.push_back(node_i); node_i = dijkstra_graph.predecessor(node_i); } @@ -74,7 +76,7 @@ void MehlhornGraph::add_path_to_edge( std::vector path_to_node_b; node_i = edge.node_b.internal_id; - while (not _delaunay_graph.is_terminal_id(node_i)) { + while (not _coordinate_graph.is_terminal_id(node_i)) { path_to_node_a.push_back(node_i); node_i = dijkstra_graph.predecessor(node_i); } @@ -112,7 +114,7 @@ void MehlhornGraph::kruskal_on_mehlhorn_edges() { return (id_to_edge_projection(a) < id_to_edge_projection(b)); }); Disjoint_Set disjoint_set; - disjoint_set.make_sets(_delaunay_graph.num_nodes()); + disjoint_set.make_sets(_coordinate_graph.num_nodes()); for (auto edge_id: edge_ids) { auto const &edge = id_to_edge_projection(edge_id); @@ -126,4 +128,17 @@ void MehlhornGraph::kruskal_on_mehlhorn_edges() { _mehlhorn_edges = included_edges; } +CoordinateGraph MehlhornGraph::reconstruct_coord_graph_from_mehlhorn_edges() const { + auto const &nodes = _coordinate_graph.nodes(); + CoordinateGraph ret_graph(nodes, _coordinate_graph.num_terminals()); + + for (auto const &edge: _mehlhorn_edges) { + for (int i = 1; i < edge.path.size(); i++) { + ret_graph.add_edge(nodes[edge.path[i - 1]], nodes[edge.path[i]]); + } + } + + return ret_graph; +} + bool MehlhornGraph::Edge::operator<(const MehlhornGraph::Edge &other) const { return length < other.length; } diff --git a/mehlhorn/MehlhornGraph.h b/mehlhorn/MehlhornGraph.h index a0b07c4..0181a1b 100644 --- a/mehlhorn/MehlhornGraph.h +++ b/mehlhorn/MehlhornGraph.h @@ -24,12 +24,14 @@ class MehlhornGraph { [[nodiscard]] bool operator<(Edge const &other) const; }; - explicit MehlhornGraph(DelaunayGraph const &delaunay_graph) : _delaunay_graph(delaunay_graph) {} + explicit MehlhornGraph(CoordinateGraph const &coordinate_graph) : _coordinate_graph(coordinate_graph) {} void calculate_mehlhorn_graph(); void kruskal_on_mehlhorn_edges(); + [[nodiscard]] CoordinateGraph reconstruct_coord_graph_from_mehlhorn_edges() const; + private: template @@ -38,7 +40,7 @@ class MehlhornGraph { static void bucket_sort(std::vector &vector, TypeToNodeId type_to_node_id, NodeId max_id); - DelaunayGraph const &_delaunay_graph; + CoordinateGraph const &_coordinate_graph; std::vector _mehlhorn_edges; void add_path_to_edge(const DijkstraGraph &dijkstra_graph, const DelaunayGraph::Edge &edge, Edge &candidate_edge) const; From c7d81e2d6fb771bf6abba8db5964050c86ada8fe Mon Sep 17 00:00:00 2001 From: JakobGie <44549596+armytrong@users.noreply.github.com> Date: Wed, 6 Jul 2022 01:02:39 +0200 Subject: [PATCH 22/25] implemented l-shape-flipping --- STPFileParser.cpp | 4 +- delaunay/DelaunayGraph.cpp | 37 +-------- graph/CoordinateGraph.cpp | 160 ++++++++++++++++++++++++++++++++++++- graph/CoordinateGraph.h | 7 +- main.cpp | 8 ++ 5 files changed, 177 insertions(+), 39 deletions(-) diff --git a/STPFileParser.cpp b/STPFileParser.cpp index 628a799..8f504f7 100644 --- a/STPFileParser.cpp +++ b/STPFileParser.cpp @@ -160,7 +160,7 @@ void STPFileParser::read_coordinates_from_file(std::istream &file, bool nodes_ar if (nodes_are_terminals) { _terminals.push_back((y_coord /* -1 */) * 10000 + x_coord /* -1 */ ); } else { - _node_coords[node_id] = {x_coord, y_coord}; + _node_coords[node_id - 1] = {x_coord, y_coord}; } } } @@ -172,7 +172,7 @@ void STPFileParser::read_coordinates_from_file(std::istream &file, bool nodes_ar } inline void STPFileParser::remove_carriage_return(std::string &s) { - while (s[s.size() - 1] == '\r') { + while (not s.empty() and s[s.size() - 1] == '\r') { s = s.erase(s.size() - 1); } } diff --git a/delaunay/DelaunayGraph.cpp b/delaunay/DelaunayGraph.cpp index 8ea3d53..b702c01 100644 --- a/delaunay/DelaunayGraph.cpp +++ b/delaunay/DelaunayGraph.cpp @@ -114,22 +114,16 @@ void DelaunayGraph::add_steiner_points() { edges_by_node_id[edge.node_b.internal_id].emplace_back(edge); } - auto const mid = [](GridUnit a, GridUnit b, GridUnit c) { - return std::max( - std::max(std::min(a, b), std::min(a, c)), - std::min(b, c)); - }; - const NodeId orig_num_nodes = num_nodes(); for (NodeId node_id = 0; node_id < orig_num_nodes; node_id++) { - auto const &node = _nodes[node_id]; + auto const node = _nodes[node_id]; auto const &edges = edges_by_node_id[node.internal_id]; for (int j = 0; j < edges.size(); j++) { auto const &edge_a = edges[j]; - auto const &node_a = edge_a.node_a == node ? edge_a.node_b : edge_a.node_a; + auto const node_a = edge_a.node_a == node ? edge_a.node_b : edge_a.node_a; for (int k = j + 1; k < edges.size(); k++) { auto const &edge_b = edges[k]; - auto const &node_b = edge_b.node_a == node ? edge_b.node_b : edge_b.node_a; + auto const node_b = edge_b.node_a == node ? edge_b.node_b : edge_b.node_a; auto const center_x = mid(node.x_coord, node_a.x_coord, node_b.x_coord); auto const center_y = mid(node.y_coord, node_a.y_coord, node_b.y_coord); @@ -139,28 +133,3 @@ void DelaunayGraph::add_steiner_points() { } } } - - -bool DelaunayGraph::Node::operator==(const DelaunayGraph::Node &other) const { - return x_coord == other.x_coord and y_coord == other.y_coord; -} - -bool DelaunayGraph::Node::operator<(const DelaunayGraph::Node &other) const { - return x_coord < other.x_coord or (x_coord == other.x_coord and y_coord < other.y_coord); -} - -GridUnit DelaunayGraph::Node::distance(const DelaunayGraph::Node &other) const { - return std::abs(x_coord - other.x_coord) + std::abs(y_coord - other.y_coord); -} - -bool DelaunayGraph::Edge::operator<(const DelaunayGraph::Edge &other) const { - return node_a.distance(node_b) < other.node_a.distance((other.node_b)); -} - -bool DelaunayGraph::Edge::operator==(Edge const &other) const { - return node_a == other.node_a and node_b == other.node_b; -} - -WeightType DelaunayGraph::Edge::length() const { - return std::abs(node_a.x_coord - node_b.x_coord) + std::abs(node_a.y_coord - node_b.y_coord); -} diff --git a/graph/CoordinateGraph.cpp b/graph/CoordinateGraph.cpp index b5f5e6f..63b3317 100644 --- a/graph/CoordinateGraph.cpp +++ b/graph/CoordinateGraph.cpp @@ -3,9 +3,12 @@ // #include "CoordinateGraph.h" +#include "kruskal/disjoint_set_2.h" #include #include #include +#include +#include CoordinateGraph::CoordinateGraph(std::vector nodes, NodeId num_terminals) : _max_x(0), _max_y(0), _orig_max_x(0), _orig_max_y(0), _nodes(std::move(nodes)), _num_terminals(num_terminals) { @@ -90,12 +93,13 @@ void CoordinateGraph::add_edge(CoordinateGraph::Node node_a, CoordinateGraph::No _edges.push_back({node_a, node_b}); } -void CoordinateGraph::add_node(GridUnit x_coord, GridUnit y_coord) { +CoordinateGraph::Node CoordinateGraph::add_node(GridUnit x_coord, GridUnit y_coord) { _nodes.emplace_back(x_coord, y_coord, _nodes.size()); _max_x = std::max(_max_x, std::abs(x_coord)); _max_y = std::max(_max_y, std::abs(y_coord)); _orig_max_x = _max_x; _orig_max_y = _max_y; + return _nodes.back(); } void CoordinateGraph::add_terminal(GridUnit x_coord, GridUnit y_coord) { @@ -105,4 +109,156 @@ void CoordinateGraph::add_terminal(GridUnit x_coord, GridUnit y_coord) { _nodes.emplace(_nodes.begin() + _num_terminals, x_coord, y_coord, _nodes.size()); } _num_terminals += 1; -} \ No newline at end of file +} + +void CoordinateGraph::kruskal() { + std::vector edge_ids(num_edges()); + std::iota(edge_ids.begin(), edge_ids.end(), 0); + + std::vector included_edges; + + auto id_to_edge_projection = [this](auto const edge_id) { return _edges[edge_id]; }; + + std::sort(edge_ids.begin(), edge_ids.end(), + [&id_to_edge_projection](EdgeId a, EdgeId b) { + return (id_to_edge_projection(a) < id_to_edge_projection(b)); + }); + Disjoint_Set set; + set.make_sets(num_nodes()); + for (auto edge_id: edge_ids) { + auto const &edge = id_to_edge_projection(edge_id); + + if (!set.set_equals(edge.node_a.internal_id, edge.node_b.internal_id)) { + // unite the sets of the parents of the nodes of edge + set.unite(edge.node_a.internal_id, edge.node_b.internal_id); + // add edge to the vector of included edges + included_edges.push_back(edge); + } + } + _edges = included_edges; +} + +void CoordinateGraph::l_shape_flipping() { + + auto const id_to_edge_projection = [this](EdgeId id) { + assert(id < num_edges()); + return _edges[id]; + }; + + std::vector> adjacence_vectors(num_nodes()); + + + std::vector edge_deleted(num_edges(), false); + + for (EdgeId i = 0; i < num_edges(); i++) { + Edge const &edge = _edges[i]; + adjacence_vectors[edge.node_a.internal_id].push_back(i); + adjacence_vectors[edge.node_b.internal_id].push_back(i); + } + + auto const orig_num_nodes = num_nodes(); + auto const orig_num_edges = num_edges(); + for (NodeId node_id = 0; node_id < orig_num_nodes; node_id++) { + auto const node = _nodes[node_id]; + auto const &adjacent_edge_ids = adjacence_vectors[node_id]; + for (size_t j = 0; j < adjacent_edge_ids.size(); j++) { + auto const edge_a_id = adjacent_edge_ids[j]; + if (edge_deleted[edge_a_id]) continue; + auto const edge_a = id_to_edge_projection(edge_a_id); + auto const node_a = edge_a.node_a == node ? edge_a.node_b : edge_a.node_a; + + for (size_t k = j + 1; k < adjacent_edge_ids.size(); k++) { + auto const edge_b_id = adjacent_edge_ids[k]; + if (edge_deleted[edge_b_id]) continue; + auto const edge_b = id_to_edge_projection(edge_b_id); + auto const node_b = edge_b.node_a == node ? edge_b.node_b : edge_b.node_a; + + auto const old_edge_len = edge_a.length() + edge_b.length(); + auto const flipped_edge_len = ( + std::max(std::max(node_a.x_coord, node_b.x_coord), node.x_coord) - + std::min(std::min(node_a.x_coord, node_b.x_coord), node.x_coord) + + std::max(std::max(node_a.y_coord, node_b.y_coord), node.y_coord) - + std::min(std::min(node_a.y_coord, node_b.y_coord), node.y_coord) + ); + + if (old_edge_len > flipped_edge_len) { + auto const steiner_point = add_node(mid(node_a.x_coord, node_b.x_coord, node.x_coord), + mid(node_a.y_coord, node_b.y_coord, node.y_coord)); + add_edge(node, steiner_point); + add_edge(node_a, steiner_point); + add_edge(node_b, steiner_point); + + edge_deleted[edge_a_id] = true; + edge_deleted[edge_b_id] = true; + } + } + } + } + + std::vector new_edges; + for (EdgeId i = 0; i < num_edges(); i++) { + if (i >= orig_num_edges or (not edge_deleted[i])) { + new_edges.push_back(_edges[i]); + } + } + _edges = new_edges; +} + + +GridUnit CoordinateGraph::mid(GridUnit a, GridUnit b, GridUnit c) { + return std::max( + std::max(std::min(a, b), std::min(a, c)), + std::min(b, c)); +} + +void CoordinateGraph::reduce_nodes() { + NodeId orig_num_nodes = num_nodes(); + std::vector new_node_id(num_nodes(), -1); + std::iota(new_node_id.begin(), new_node_id.begin() + num_terminals() - 1, 0); + NodeId node_id_counter = num_terminals(); + for (auto &edge: _edges) { + if (new_node_id[edge.node_a.internal_id] == -1) { + new_node_id[edge.node_a.internal_id] = node_id_counter; + node_id_counter += 1; + } + if (new_node_id[edge.node_b.internal_id] == -1) { + new_node_id[edge.node_b.internal_id] = node_id_counter; + node_id_counter += 1; + } + edge.node_a.internal_id = new_node_id[edge.node_a.internal_id]; + edge.node_b.internal_id = new_node_id[edge.node_b.internal_id]; + } + std::vector new_nodes(node_id_counter, {-1, -1, -1}); + for (int i = 0; i < new_node_id.size(); i++) { + if (new_node_id[i] != -1) { + auto const &old_node = _nodes[i]; + new_nodes[new_node_id[i]] = {old_node.x_coord, old_node.y_coord, new_node_id[i]}; + } + } +} + + +bool CoordinateGraph::Node::operator==(const CoordinateGraph::Node &other) const { + return x_coord == other.x_coord and y_coord == other.y_coord; +} + +bool CoordinateGraph::Node::operator<(const CoordinateGraph::Node &other) const { + return x_coord < other.x_coord or (x_coord == other.x_coord and y_coord < other.y_coord); +} + +GridUnit CoordinateGraph::Node::distance(const CoordinateGraph::Node &other) const { + return std::abs(x_coord - other.x_coord) + std::abs(y_coord - other.y_coord); +} + +bool CoordinateGraph::Edge::operator<(const CoordinateGraph::Edge &other) const { + return node_a.distance(node_b) < other.node_a.distance((other.node_b)); +} + +bool CoordinateGraph::Edge::operator==(Edge const &other) const { + return node_a == other.node_a and node_b == other.node_b; +} + +WeightType CoordinateGraph::Edge::length() const { + return std::abs(node_a.x_coord - node_b.x_coord) + std::abs(node_a.y_coord - node_b.y_coord); +} + diff --git a/graph/CoordinateGraph.h b/graph/CoordinateGraph.h index 8525e27..948c4a7 100644 --- a/graph/CoordinateGraph.h +++ b/graph/CoordinateGraph.h @@ -50,11 +50,16 @@ class CoordinateGraph { [[nodiscard]] bool is_terminal_id(NodeId node_id) const; - void add_node(GridUnit x_coord, GridUnit y_coord); + Node add_node(GridUnit x_coord, GridUnit y_coord); void add_terminal(GridUnit x_coord, GridUnit y_coord); void add_edge(Node node_a, Node node_b); + void kruskal(); + void l_shape_flipping(); + void reduce_nodes(); protected: + static GridUnit mid(GridUnit a, GridUnit b, GridUnit c); + std::vector _nodes; std::vector _edges; GridUnit _max_x, _max_y, _orig_max_x, _orig_max_y; diff --git a/main.cpp b/main.cpp index 25527fa..2b3f2f5 100644 --- a/main.cpp +++ b/main.cpp @@ -39,6 +39,14 @@ int main(int argc, char *argv[]) { print_verbose("first kruskal complete"); auto coordinate_graph = mehlhorn_graph.reconstruct_coord_graph_from_mehlhorn_edges(); + coordinate_graph.kruskal(); + print_verbose("second kruskal complete"); + + coordinate_graph.l_shape_flipping(); + print_verbose("l-shape-flipping complete"); + + coordinate_graph.reduce_nodes(); + std::ofstream file("Output/output.ps"); coordinate_graph.print_as_postscript(file, "PostScript/template.ps"); file.close(); From 68e8733e1e1076936b7c6084e57bdc4d0a1e6caf Mon Sep 17 00:00:00 2001 From: JakobGie <44549596+armytrong@users.noreply.github.com> Date: Sat, 9 Jul 2022 09:23:19 +0200 Subject: [PATCH 23/25] improved file parser to read multiple instances --- .gitignore | 6 +- ArgumentHandler.cpp | 3 + ArgumentHandler.h | 1 + CMakeLists.txt | 2 + STPFileParser.cpp | 136 +++++++++++++++++++------------------- STPFileParser.h | 8 ++- graph/CoordinateGraph.cpp | 62 +++++++++++++---- graph/CoordinateGraph.h | 1 + main.cpp | 123 +++++++++++++++++++--------------- 9 files changed, 204 insertions(+), 138 deletions(-) diff --git a/.gitignore b/.gitignore index e6d6228..69173ca 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,8 @@ 13_hard/ bench_instances/ /PostScript/test.ps -/Output/ \ No newline at end of file +/Output/ +/RSMT-ibm/ +*.stp +*.ps +!PostScript/template.ps diff --git a/ArgumentHandler.cpp b/ArgumentHandler.cpp index f0b246f..f82effe 100644 --- a/ArgumentHandler.cpp +++ b/ArgumentHandler.cpp @@ -16,6 +16,7 @@ ArgumentHandler::ArgumentHandler(int argc, char **argv) : _parms{ 0, false, false, + true, true } { for (int i = 1; i < argc; i++) { @@ -47,6 +48,8 @@ ArgumentHandler::ArgumentHandler(int argc, char **argv) : _parms{ if (i < argc) { _parms.instances_to_skip = std::stoi(argv[i]); } + } else if (arg == "--silent") { + _parms.output = false; } else if (arg[0] == '-') { std::cerr << argv[0] << ": " << arg << ": invalid option" << std::endl; print_usage(std::cerr, argv[0]); diff --git a/ArgumentHandler.h b/ArgumentHandler.h index 33eff6a..80c8cdd 100644 --- a/ArgumentHandler.h +++ b/ArgumentHandler.h @@ -24,6 +24,7 @@ class ArgumentHandler { size_t instances_to_skip; bool nodes_are_terminals; bool verbose; + bool output; bool run; }; diff --git a/CMakeLists.txt b/CMakeLists.txt index b9a1e89..e489b04 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,6 +3,8 @@ project(Hauptaufgabe_2) set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined -fsanitize=address -g") + include_directories(.) add_executable(Hauptaufgabe_2 diff --git a/STPFileParser.cpp b/STPFileParser.cpp index 8f504f7..3a8dfaa 100644 --- a/STPFileParser.cpp +++ b/STPFileParser.cpp @@ -10,75 +10,9 @@ #include #include "STPFileParser.h" -constexpr char FILE_HEADER[] = "33D32945 STP File, STP Format Version 1.0"; -STPFileParser::STPFileParser(ArgumentHandler::Parms const &parms) : _filename(std::move(parms.input_filename)) { - - _num_nodes = -1; - - std::ifstream file(_filename); - if (not file and not _filename.empty()) { - throw std::invalid_argument("input file not found"); - } - std::istream &input = _filename.empty() ? std::cin : file; - - // skip the required number of instances in the input - std::string line; - safe_getline(input, line); // read the first line of the input - for (size_t i = 0; i < parms.instances_to_skip + 1; i++) { - while (line != FILE_HEADER and not input.eof()) { - safe_getline(input, line); - remove_carriage_return(line); - } - safe_getline(input, line); - } - - if (input.eof()) { - throw std::invalid_argument("Invalid input format: No stp-instance found"); - } - - bool graph_found = false; - bool terminals_found = false; - bool coordinates_found = false; - - while (line != "EOF") { - if (not safe_getline(input, line)) { - throw std::invalid_argument("Invalid input format: EOF expected"); - } - if (line == "SECTION Graph" or line == "Section Graph") { - if (graph_found) { - throw std::invalid_argument("Invalid input format: Multiple graphs in input."); - } else { - read_graph_from_file(input); - graph_found = true; - } - } else if (line == "SECTION Terminals" or line == "Section Terminals") { - if (terminals_found) { - throw std::invalid_argument("Invalid input format: Multiple terminal sections in input."); - } else { - read_terminals_from_file(input); - terminals_found = true; - } - } else if (line == "SECTION Coordinates" or line == "Section Coordinates") { - if (coordinates_found) { - throw std::invalid_argument("Invalid input format: Multiple coordinate sections in input."); - } - if (parms.nodes_are_terminals) { - _num_terminals = _num_nodes; - _num_nodes = 10000 * 10000; - read_coordinates_from_file(input, true); - } else { - read_coordinates_from_file(input, false); - } - coordinates_found = true; - } - } - - if (!graph_found) { - throw std::invalid_argument("Invalid input format: No graph in input."); - } - -} +STPFileParser::STPFileParser(ArgumentHandler::Parms const &parms) : + _parms(parms), _num_nodes(-1), _num_terminals(-1) {} [[maybe_unused]] Graph STPFileParser::create_graph() { return {_num_nodes, _terminals, _edges}; @@ -199,4 +133,70 @@ inline std::istream &STPFileParser::safe_getline(std::istream &istream, std::str return ret; } +bool STPFileParser::read_next_instance(std::istream &istream) { + _num_nodes = -1; + _num_terminals = -1; + _terminals.clear(); + _edges.clear(); + _node_coords.clear(); + + // skip the required number of instances in the input + std::string line; + safe_getline(istream, line); // read the first line of the input + for (size_t i = 0; i < _parms.instances_to_skip + 1; i++) { + while (line != FILE_HEADER and not istream.eof()) { + safe_getline(istream, line); + remove_carriage_return(line); + } + safe_getline(istream, line); + } + + if (istream.eof()) { + return false; + } + + bool graph_found = false; + bool terminals_found = false; + bool coordinates_found = false; + + while (line != "EOF") { + if (not safe_getline(istream, line)) { + throw std::invalid_argument("Invalid input format: EOF expected"); + } + if (line == "SECTION Graph" or line == "Section Graph") { + if (graph_found) { + throw std::invalid_argument("Invalid input format: Multiple graphs in input."); + } else { + read_graph_from_file(istream); + graph_found = true; + } + } else if (line == "SECTION Terminals" or line == "Section Terminals") { + if (terminals_found) { + throw std::invalid_argument("Invalid input format: Multiple terminal sections in input."); + } else { + read_terminals_from_file(istream); + terminals_found = true; + } + } else if (line == "SECTION Coordinates" or line == "Section Coordinates") { + if (coordinates_found) { + throw std::invalid_argument("Invalid input format: Multiple coordinate sections in input."); + } + if (_parms.nodes_are_terminals) { + _num_terminals = _num_nodes; + _num_nodes = 10000 * 10000; + read_coordinates_from_file(istream, true); + } else { + read_coordinates_from_file(istream, false); + } + coordinates_found = true; + } + } + + if (!graph_found) { + throw std::invalid_argument("Invalid input format: No graph in input."); + } + return true; +} + + diff --git a/STPFileParser.h b/STPFileParser.h index 8a36890..e11c409 100644 --- a/STPFileParser.h +++ b/STPFileParser.h @@ -14,12 +14,17 @@ class STPFileParser { public: + static constexpr char FILE_HEADER[] = "33D32945 STP File, STP Format Version 1.0"; + explicit STPFileParser(ArgumentHandler::Parms const &parms); + bool read_next_instance(std::istream &istream); + [[maybe_unused]] Graph create_graph(); DelaunayGraph create_delaunay_graph(); + private: void read_graph_from_file(std::istream &file); @@ -32,12 +37,13 @@ class STPFileParser { static std::istream &safe_getline(std::istream &istream, std::string &string); - std::string _filename; + ArgumentHandler::Parms const &_parms; NodeId _num_nodes; NodeId _num_terminals; std::vector _terminals; std::vector _edges; std::vector _node_coords; + }; diff --git a/graph/CoordinateGraph.cpp b/graph/CoordinateGraph.cpp index 63b3317..8976ff6 100644 --- a/graph/CoordinateGraph.cpp +++ b/graph/CoordinateGraph.cpp @@ -4,6 +4,7 @@ #include "CoordinateGraph.h" #include "kruskal/disjoint_set_2.h" +#include "STPFileParser.h" #include #include #include @@ -48,32 +49,32 @@ void CoordinateGraph::print_as_postscript(std::ostream &os, const std::string &b auto grid_width = static_cast(std::sqrt(std::max(_orig_max_x, _orig_max_y))); - os << std::endl; - os << "%%BeginSetup" << std::endl << std::endl; + os << "\n"; + os << "%%BeginSetup" << "\n" << "\n"; os << 0 << " " << (grid_width + 1) * (grid_width + 1) << " " << 0 << " " << (grid_width + 1) * (grid_width + 1) - << " SetAxes" << std::endl << std::endl; + << " SetAxes" << "\n" << "\n"; - os << num_terminals() << " DefineTerminals" << std::endl; + os << num_terminals() << " DefineTerminals" << "\n"; for (auto terminal: _nodes) { if (is_terminal_id(terminal.internal_id)) { - os << "\t" << terminal.x_coord + 1 << "\t" << terminal.y_coord + 1 << "\tDT" << std::endl; + os << "\t" << terminal.x_coord + 1 << "\t" << terminal.y_coord + 1 << "\tDT" << "\n"; } } - os << std::endl << "%%EndSetup" << std::endl << std::endl; + os << "\n" << "%%EndSetup" << "\n" << "\n"; - os << "%%Page: 1 1" << std::endl; - os << "BeginPlot" << std::endl; - os << "\tPlot_Terminals" << std::endl; + os << "%%Page: 1 1" << "\n"; + os << "BeginPlot" << "\n"; + os << "\tPlot_Terminals" << "\n"; for (auto const &edge: _edges) { os << "\t" << edge.node_a.x_coord + 1 << "\t" << edge.node_a.y_coord + 1 << "\t" << edge.node_b.x_coord + 1 - << "\t" << edge.node_b.y_coord + 1 << "\tS" << std::endl; + << "\t" << edge.node_b.y_coord + 1 << "\tS" << "\n"; } //os << " (Steiner Minimal Tree: " << _terminals.size() << "points, length=" << grid_width * grid_widht << ")" << - //std::endl; + //"\n"; os << "EndPlot" << std::endl; } @@ -213,8 +214,8 @@ GridUnit CoordinateGraph::mid(GridUnit a, GridUnit b, GridUnit c) { void CoordinateGraph::reduce_nodes() { NodeId orig_num_nodes = num_nodes(); - std::vector new_node_id(num_nodes(), -1); - std::iota(new_node_id.begin(), new_node_id.begin() + num_terminals() - 1, 0); + std::vector new_node_id(orig_num_nodes, -1); + std::iota(new_node_id.begin(), new_node_id.begin() + num_terminals(), 0); NodeId node_id_counter = num_terminals(); for (auto &edge: _edges) { if (new_node_id[edge.node_a.internal_id] == -1) { @@ -235,6 +236,41 @@ void CoordinateGraph::reduce_nodes() { new_nodes[new_node_id[i]] = {old_node.x_coord, old_node.y_coord, new_node_id[i]}; } } + _nodes = new_nodes; +} + +void CoordinateGraph::print_as_stp(std::ostream &outstream) { + outstream << STPFileParser::FILE_HEADER << "\n"; + outstream << "\n"; + + outstream << "SECTION Graph" << "\n"; + outstream << "Nodes " << num_nodes() << "\n"; + outstream << "Edges " << _edges.size() << "\n"; + for (auto const &edge: _edges) { + outstream << "E " << edge.node_a.internal_id + 1 + << " " << edge.node_b.internal_id + 1 + << " " << edge.length() << "\n"; + } + outstream << "END" << "\n"; + outstream << "\n"; + + outstream << "SECTION Terminals" << "\n"; + outstream << "Terminals " << num_terminals() << "\n"; + for (int t = 0; t < num_terminals(); t++) { + outstream << "T " << t + 1 << "\n"; + } + outstream << "END" << "\n"; + outstream << "\n"; + + outstream << "SECTION Coordinates" << "\n"; + for (auto const &node: _nodes) { + outstream << "DD " << node.internal_id + 1 + << " " << node.x_coord + << " " << node.y_coord << "\n"; + } + outstream << "END" << "\n" << "\n"; + + outstream << "EOF" << std::endl; } diff --git a/graph/CoordinateGraph.h b/graph/CoordinateGraph.h index 948c4a7..056560e 100644 --- a/graph/CoordinateGraph.h +++ b/graph/CoordinateGraph.h @@ -41,6 +41,7 @@ class CoordinateGraph { [[maybe_unused]] [[nodiscard]] Graph export_graph() const; [[maybe_unused]] void print_as_postscript(std::ostream &os, const std::string &base_file_name); + void print_as_stp(std::ostream &os); [[nodiscard]] NodeId num_nodes() const; [[nodiscard]] NodeId num_terminals() const; diff --git a/main.cpp b/main.cpp index 2b3f2f5..1087b21 100644 --- a/main.cpp +++ b/main.cpp @@ -1,7 +1,5 @@ #include #include "STPFileParser.h" -#include "kruskal/kruskal_ind.h" -#include "graph/RectilinearGraph.h" #include "ArgumentHandler.h" #include "mehlhorn/MehlhornGraph.h" @@ -11,68 +9,83 @@ int main(int argc, char *argv[]) { if (not parms.run) return 0; - auto print_verbose = [& parms](std::string const &s) { if (parms.verbose) std::cout << s << std::endl; }; + std::ifstream ifile(parms.input_filename); + std::istream &istream = (parms.input_filename.empty() or not ifile) ? std::cin : ifile; + if (not ifile and not parms.input_filename.empty()) { + std::cerr << "unable to open input file, falling back to standard input" << std::endl; + } + + std::ofstream ofile(parms.output_filename); + std::ostream &ostream = (parms.output_filename.empty() or not ofile) ? std::cout : ofile; + if (not ofile and not parms.output_filename.empty()) { + std::cerr << "unable to open output file, falling back to standard output" << std::endl; + } + + + auto print_verbose = [& parms](std::string const &s) { + if (parms.verbose) + std::cout << "[Verbose]: " << s << std::endl; + }; - DelaunayGraph delaunay_graph; + + STPFileParser parser(parms); + bool instance_found = false; try { - STPFileParser parser(parms); + instance_found = parser.read_next_instance(istream); print_verbose("input parsed"); - delaunay_graph = parser.create_delaunay_graph(); } catch (std::invalid_argument const &e) { std::cerr << "Failed to read input. Error: " << e.what() << std::endl; return 1; } - delaunay_graph.calculate_l1_delaunay_triangulation(); - delaunay_graph.add_steiner_points(); - delaunay_graph.calculate_l1_delaunay_triangulation(); - print_verbose("Delaunay triangulation complete."); - - - MehlhornGraph mehlhorn_graph(delaunay_graph); - mehlhorn_graph.calculate_mehlhorn_graph(); - print_verbose("Mehlhorn Graph calculated."); -// std::ofstream file("Output/output.ps"); -// delaunay_graph.print_as_postscript(file, "PostScript/template.ps"); -// file.close(); - mehlhorn_graph.kruskal_on_mehlhorn_edges(); - print_verbose("first kruskal complete"); - - auto coordinate_graph = mehlhorn_graph.reconstruct_coord_graph_from_mehlhorn_edges(); - coordinate_graph.kruskal(); - print_verbose("second kruskal complete"); - - coordinate_graph.l_shape_flipping(); - print_verbose("l-shape-flipping complete"); - - coordinate_graph.reduce_nodes(); - - std::ofstream file("Output/output.ps"); - coordinate_graph.print_as_postscript(file, "PostScript/template.ps"); - file.close(); -// RectilinearGraph rect_graph(kruskal_graph.export_graph()); -// print_verbose("constructed rectiliear graph"); -// -// auto graph2 = rect_graph.export_graph(); -// auto kruskal_graph2 = kruskal(graph2); -// print_verbose("second kruskal complete"); -// -// std::cout << std::endl; -// -// std::ofstream file(parms.output_filename); -// std::ostream &ostream = (parms.output_filename.empty() or not file) ? std::cout : file; -// if (not file and not parms.output_filename.empty()) { -// std::cerr << "unable to open output file, falling back to standard output" << std::endl; -// } -// -// if (parms.output_format == OutputFormat::STP) { -// kruskal_graph2.print_graph(ostream); -// } else if (parms.output_format == OutputFormat::PS) { -// RectilinearGraph output_graph(kruskal_graph2); -// output_graph.print_as_postscript(ostream, "PostScript/template.ps"); -// } -// file.close(); + int instance_counter = 0; + + while (instance_found) { + + print_verbose("Starting Calculation of instance " + std::to_string(instance_counter)); + auto delaunay_graph = parser.create_delaunay_graph(); + + delaunay_graph.calculate_l1_delaunay_triangulation(); + delaunay_graph.add_steiner_points(); + delaunay_graph.calculate_l1_delaunay_triangulation(); + print_verbose("Delaunay triangulation complete"); + + MehlhornGraph mehlhorn_graph(delaunay_graph); + mehlhorn_graph.calculate_mehlhorn_graph(); + print_verbose("Mehlhorn Graph calculated"); + mehlhorn_graph.kruskal_on_mehlhorn_edges(); + print_verbose("first kruskal complete"); + + auto coordinate_graph = mehlhorn_graph.reconstruct_coord_graph_from_mehlhorn_edges(); + coordinate_graph.kruskal(); + print_verbose("second kruskal complete"); + + coordinate_graph.l_shape_flipping(); + coordinate_graph.l_shape_flipping(); + print_verbose("l-shape-flipping complete"); + + + coordinate_graph.reduce_nodes(); + print_verbose("finished calculation of intance " + std::to_string(instance_counter++) + "\n"); + + if (parms.output_format == OutputFormat::STP and parms.output) { + coordinate_graph.print_as_stp(ostream); + } else if (parms.output_format == OutputFormat::PS and parms.output) { + coordinate_graph.print_as_postscript(ostream, "PostScript/template.ps"); + } + ostream << std::endl << std::endl; + + try { + instance_found = parser.read_next_instance(istream); + if (instance_found) print_verbose("input parsed"); + } catch (std::invalid_argument const &e) { + std::cerr << "Failed to read input. Error: " << e.what() << std::endl; + return 1; + } + } + if (ifile) ifile.close(); + if (ofile) ofile.close(); return 0; } From db07890dc71d8f779213fbf48d5c197d784245ad Mon Sep 17 00:00:00 2001 From: JakobGie <44549596+armytrong@users.noreply.github.com> Date: Sat, 9 Jul 2022 10:20:12 +0200 Subject: [PATCH 24/25] refactorings from comments --- CMakeLists.txt | 2 +- STPFileParser.cpp | 6 +- delaunay/DelaunayGraph.cpp | 65 ++++++++++++--------- delaunay/DelaunayGraph.h | 3 +- delaunay/DelaunayPriorityQueue.cpp | 14 +++-- delaunay/DelaunayPriorityQueue.h | 4 +- delaunay/DelaunaySet.cpp | 24 ++++---- delaunay/DelaunaySet.h | 18 +++--- dijkstra/DijkstraGraph.cpp | 16 ++--- dijkstra/DijkstraGraph.h | 8 +-- graph/CoordinateGraph.cpp | 14 ++--- graph/CoordinateGraph.h | 2 +- graph/RectilinearGraph.cpp | 10 ++-- kruskal/{disjoint_set_2.h => DisjointSet.h} | 22 +++---- kruskal/kruskal_ind.cpp | 6 +- main.cpp | 3 +- mehlhorn/MehlhornGraph.cpp | 4 +- typedefs.h | 4 ++ 18 files changed, 122 insertions(+), 103 deletions(-) rename kruskal/{disjoint_set_2.h => DisjointSet.h} (77%) diff --git a/CMakeLists.txt b/CMakeLists.txt index e489b04..22ff0ef 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,4 +34,4 @@ add_executable(Hauptaufgabe_2 delaunay/DelaunayPriorityQueue.cpp delaunay/DelaunayPriorityQueue.h ArgumentHandler.cpp - ArgumentHandler.h mehlhorn/MehlhornGraph.cpp mehlhorn/MehlhornGraph.h graph/CoordinateGraph.cpp graph/CoordinateGraph.h) + ArgumentHandler.h mehlhorn/MehlhornGraph.cpp mehlhorn/MehlhornGraph.h graph/CoordinateGraph.cpp graph/CoordinateGraph.h mehlhorn/mehlhorn.cpp mehlhorn/mehlhorn.h) diff --git a/STPFileParser.cpp b/STPFileParser.cpp index 3a8dfaa..45c7ce9 100644 --- a/STPFileParser.cpp +++ b/STPFileParser.cpp @@ -12,7 +12,7 @@ STPFileParser::STPFileParser(ArgumentHandler::Parms const &parms) : - _parms(parms), _num_nodes(-1), _num_terminals(-1) {} + _parms(parms), _num_nodes(INVALID_NODE), _num_terminals(INVALID_NODE) {} [[maybe_unused]] Graph STPFileParser::create_graph() { return {_num_nodes, _terminals, _edges}; @@ -134,8 +134,8 @@ inline std::istream &STPFileParser::safe_getline(std::istream &istream, std::str } bool STPFileParser::read_next_instance(std::istream &istream) { - _num_nodes = -1; - _num_terminals = -1; + _num_nodes = INVALID_NODE; + _num_terminals = INVALID_NODE; _terminals.clear(); _edges.clear(); _node_coords.clear(); diff --git a/delaunay/DelaunayGraph.cpp b/delaunay/DelaunayGraph.cpp index b702c01..2fd0e77 100644 --- a/delaunay/DelaunayGraph.cpp +++ b/delaunay/DelaunayGraph.cpp @@ -8,62 +8,69 @@ #include "DelaunaySet.h" #include #include -#include void DelaunayGraph::calculate_l1_delaunay_triangulation() { translate_from_1_to_infty_norm(); _edges.clear(); - DelaunayPriorityQueue X(_max_x, _max_y); + DelaunayPriorityQueue x_record_queue(_max_x, _max_y); for (auto const &terminal: _nodes) { - X.insert(terminal, terminal.x_coord, ACTIVE); + x_record_queue.insert(terminal, terminal.x_coord, ActiveInactive::ACTIVE); } - DelaunaySet Y; - Y.insert({-_max_x - 1, -_max_y - 1}); - Y.insert({-_max_x - 1, _max_y + 1}); + DelaunaySet y_node_set; + y_node_set.insert({-_max_x - 1, -_max_y - 1}); + y_node_set.insert({-_max_x - 1, _max_y + 1}); std::map ages; int age_counter = 0; - while (not X.empty()) { - auto P = X.extract_min(); - ages.insert(std::make_pair(P.terminal, age_counter)); + while (not x_record_queue.empty()) { + auto const minimum_record = x_record_queue.extract_min(); + ages.insert(std::make_pair(minimum_record.terminal, age_counter)); age_counter++; - if (P.status == ACTIVE) { - Y.insert(P.terminal); - auto successor = Y.successor(P.terminal); - auto predecessor = Y.predecessor((P.terminal)); + if (minimum_record.status == ActiveInactive::ACTIVE) { + y_node_set.insert(minimum_record.terminal); + auto const successor = y_node_set.successor(minimum_record.terminal); + auto const predecessor = y_node_set.predecessor((minimum_record.terminal)); assert(successor.has_value() and predecessor.has_value()); if (successor->x_coord != -_max_x - 1) { - _edges.push_back({P.terminal, *successor}); - update_inactivation_records(X, Y, ages[P.terminal] >= ages[*successor] ? *successor : P.terminal); + _edges.push_back({minimum_record.terminal, *successor}); + update_inactivation_records(x_record_queue, y_node_set, + ages[minimum_record.terminal] >= ages[*successor] ? *successor + : minimum_record.terminal); } if (predecessor->x_coord != -_max_x - 1) { - _edges.push_back({P.terminal, *predecessor}); - update_inactivation_records(X, Y, ages[P.terminal] >= ages[*predecessor] ? *predecessor : P.terminal); + _edges.push_back({minimum_record.terminal, *predecessor}); + update_inactivation_records(x_record_queue, y_node_set, + ages[minimum_record.terminal] >= ages[*predecessor] ? *predecessor + : minimum_record.terminal); } } else { - auto q = Y.predecessor(P.terminal); - Y.del(P.terminal); - auto successor = Y.successor(*q); - if (q->x_coord != -_max_x - 1 and successor->x_coord != -_max_x - 1) _edges.push_back({*q, *successor}); - update_inactivation_records(X, Y, ages[*q] >= ages[*successor] ? *successor : *q); + auto const predecessor = y_node_set.predecessor(minimum_record.terminal); + y_node_set.erase(minimum_record.terminal); + auto const successor = y_node_set.successor(*predecessor); + if (predecessor->x_coord != -_max_x - 1 and successor->x_coord != -_max_x - 1) { + _edges.push_back({*predecessor, *successor}); + } + update_inactivation_records(x_record_queue, y_node_set, + ages[*predecessor] >= ages[*successor] ? *successor : *predecessor); } } translate_from_infty_to_1_norm(); } -void DelaunayGraph::update_inactivation_records(DelaunayPriorityQueue &X, const DelaunaySet &Y, +void DelaunayGraph::update_inactivation_records(DelaunayPriorityQueue &x_record_queue, const DelaunaySet &y_node_set, DelaunayGraph::Node terminal) { - auto r = Y.predecessor(terminal); - auto q = Y.successor(terminal); - if (r->x_coord > terminal.x_coord and q->x_coord >= terminal.x_coord) { - if (X.find_inactivation_record(terminal).has_value()) { - X.change_priority(terminal, terminal.x_coord + q->y_coord - r->y_coord); + auto predecessor = y_node_set.predecessor(terminal); + auto successor = y_node_set.successor(terminal); + if (predecessor->x_coord > terminal.x_coord and successor->x_coord >= terminal.x_coord) { + if (x_record_queue.find_inactivation_record(terminal).has_value()) { + x_record_queue.change_priority(terminal, terminal.x_coord + successor->y_coord - predecessor->y_coord); } else { - X.insert(terminal, terminal.x_coord + q->y_coord - r->y_coord, INACTIVE); + x_record_queue.insert(terminal, terminal.x_coord + successor->y_coord - predecessor->y_coord, + ActiveInactive::INACTIVE); } } } diff --git a/delaunay/DelaunayGraph.h b/delaunay/DelaunayGraph.h index eaa8585..1d8b393 100644 --- a/delaunay/DelaunayGraph.h +++ b/delaunay/DelaunayGraph.h @@ -38,7 +38,8 @@ class DelaunayGraph : public CoordinateGraph { private: - static void update_inactivation_records(DelaunayPriorityQueue &X, DelaunaySet const &Y, Node terminal); + static void + update_inactivation_records(DelaunayPriorityQueue &x_record_queue, DelaunaySet const &y_node_set, Node terminal); static void translate_terminal_from_1_to_infty_norm(Node &t); diff --git a/delaunay/DelaunayPriorityQueue.cpp b/delaunay/DelaunayPriorityQueue.cpp index 2bb30c2..c0966c9 100644 --- a/delaunay/DelaunayPriorityQueue.cpp +++ b/delaunay/DelaunayPriorityQueue.cpp @@ -20,15 +20,19 @@ void DelaunayPriorityQueue::insert(const DelaunayPriorityQueue::Terminal &t, Gri std::optional DelaunayPriorityQueue::find_inactivation_record (DelaunayPriorityQueue::Terminal t) const { - auto record = *_set.lower_bound({t, 0, INACTIVE}); - return (record.terminal == t and record.status == INACTIVE) ? std::make_optional(record) : std::nullopt; + auto record = *_set.lower_bound({t, 0, ActiveInactive::INACTIVE}); + if (record.terminal == t and record.status == ActiveInactive::INACTIVE) { + return std::make_optional(record); + } else { + return std::nullopt; + } } void DelaunayPriorityQueue::change_priority(const DelaunayPriorityQueue::Terminal &t, GridUnit new_trans) { - auto record = *_set.lower_bound({t, 0, INACTIVE}); - assert(record.status == INACTIVE); + auto record = *_set.lower_bound({t, 0, ActiveInactive::INACTIVE}); + assert(record.status == ActiveInactive::INACTIVE); _set.erase(record); - _set.insert({t, new_trans, INACTIVE}); + _set.insert({t, new_trans, ActiveInactive::INACTIVE}); } bool DelaunayPriorityQueue::empty() const { diff --git a/delaunay/DelaunayPriorityQueue.h b/delaunay/DelaunayPriorityQueue.h index ac9b39d..64675bd 100644 --- a/delaunay/DelaunayPriorityQueue.h +++ b/delaunay/DelaunayPriorityQueue.h @@ -9,7 +9,7 @@ #include #include "DelaunayGraph.h" -enum ActiveInactive { +enum class ActiveInactive { INACTIVE, ACTIVE }; @@ -17,7 +17,7 @@ enum ActiveInactive { class DelaunayPriorityQueue { public: DelaunayPriorityQueue(GridUnit max_x, GridUnit max_y) : MINIMUM_RECORD( - {{-max_x - 1, -max_y - 1}, -max_x - max_y - 1, INACTIVE}) {} + {{-max_x - 1, -max_y - 1}, -max_x - max_y - 1, ActiveInactive::INACTIVE}) {} using Terminal = DelaunayGraph::Node; struct Record { diff --git a/delaunay/DelaunaySet.cpp b/delaunay/DelaunaySet.cpp index b429349..3d3b69e 100644 --- a/delaunay/DelaunaySet.cpp +++ b/delaunay/DelaunaySet.cpp @@ -5,22 +5,22 @@ #include "DelaunaySet.h" -void DelaunaySet::insert(const DelaunaySet::Terminal &t) { - _less_set.emplace(t); - _greater_set.emplace(t); +void DelaunaySet::insert(const DelaunaySet::Node &node) { + _less_set.emplace(node); + _greater_set.emplace(node); } -void DelaunaySet::del(const DelaunaySet::Terminal &t) { - _less_set.erase(t); - _greater_set.erase(t); +void DelaunaySet::erase(const DelaunaySet::Node &node) { + _less_set.erase(node); + _greater_set.erase(node); } -std::optional DelaunaySet::predecessor(const DelaunaySet::Terminal &t) const { - auto ret = *_greater_set.upper_bound(t); - return greater_fun(t, ret) ? std::make_optional(ret) : std::nullopt; +std::optional DelaunaySet::predecessor(const DelaunaySet::Node &node) const { + auto ret = *_greater_set.upper_bound(node); + return greater_fun(node, ret) ? std::make_optional(ret) : std::nullopt; } -std::optional DelaunaySet::successor(const DelaunaySet::Terminal &t) const { - auto ret = *_less_set.upper_bound(t); - return less_fun(t, ret) ? std::make_optional(ret) : std::nullopt; +std::optional DelaunaySet::successor(const DelaunaySet::Node &node) const { + auto ret = *_less_set.upper_bound(node); + return less_fun(node, ret) ? std::make_optional(ret) : std::nullopt; } diff --git a/delaunay/DelaunaySet.h b/delaunay/DelaunaySet.h index 52a48ee..d3b0374 100644 --- a/delaunay/DelaunaySet.h +++ b/delaunay/DelaunaySet.h @@ -10,23 +10,23 @@ class DelaunaySet { public: - using Terminal = DelaunayGraph::Node; + using Node = DelaunayGraph::Node; - void insert(Terminal const &t); - void del(Terminal const &t); - [[nodiscard]] std::optional successor(Terminal const &t) const; - [[nodiscard]] std::optional predecessor(Terminal const &t) const; + void insert(Node const &node); + void erase(Node const &node); + [[nodiscard]] std::optional successor(Node const &node) const; + [[nodiscard]] std::optional predecessor(Node const &node) const; private: - static constexpr auto less_fun = [](Terminal const &lhs, Terminal const &rhs) { + static constexpr auto less_fun = [](Node const &lhs, Node const &rhs) { return (lhs.y_coord < rhs.y_coord) or (rhs.y_coord == lhs.y_coord and rhs.x_coord < lhs.x_coord); }; - static constexpr auto greater_fun = [](Terminal const &lhs, Terminal const &rhs) { + static constexpr auto greater_fun = [](Node const &lhs, Node const &rhs) { return (lhs.y_coord > rhs.y_coord) or (rhs.y_coord == lhs.y_coord and rhs.x_coord > lhs.x_coord); }; - std::set _less_set; - std::set _greater_set; + std::set _less_set; + std::set _greater_set; }; diff --git a/dijkstra/DijkstraGraph.cpp b/dijkstra/DijkstraGraph.cpp index 1ea9eef..e6d3fea 100644 --- a/dijkstra/DijkstraGraph.cpp +++ b/dijkstra/DijkstraGraph.cpp @@ -14,7 +14,7 @@ DijkstraGraph::DijkstraGraph(Graph const &graph) : _calculation_finished(false) { _nodes = std::vector(graph.num_nodes()); for (size_t i = 0; i < _nodes.size(); i++) { - _nodes[i]._id = static_cast(i); + _nodes[i].id = static_cast(i); } for (auto const &edge: graph.edges()) { @@ -29,7 +29,7 @@ DijkstraGraph::DijkstraGraph(Graph const &graph) : _calculation_finished(false) DijkstraGraph::DijkstraGraph(const CoordinateGraph &coordinate_graph) : _calculation_finished(false) { _nodes = std::vector(coordinate_graph.num_nodes()); for (size_t i = 0; i < _nodes.size(); i++) { - _nodes[i]._id = static_cast(i); + _nodes[i].id = static_cast(i); } for (auto const &edge: coordinate_graph.edges()) { @@ -52,9 +52,9 @@ void DijkstraGraph::dijkstras_algorithm(NodeId root_node_id) { Node &root_node = _nodes[root_node_id]; for (auto &node: _nodes) { - node.closest_terminal = -1; - node.distance_to_root = -1; - node.predecessor = -1; + node.closest_terminal = INVALID_NODE; + node.distance_to_root = INVALID_WEIGHT; + node.predecessor = INVALID_NODE; } StandardHeap candidates; @@ -77,7 +77,7 @@ void DijkstraGraph::dijkstras_algorithm(NodeId root_node_id) { Node &node_w = _nodes[node_w_id]; - if (node_w.distance_to_root == -1) { + if (node_w.distance_to_root == INVALID_WEIGHT) { node_w.predecessor = node_v_id; node_w.distance_to_root = node_v.distance_to_root + edge_weight; @@ -126,9 +126,9 @@ void DijkstraGraph::dijkstras_algorithm(NodeId root_node_id) { std::vector terminals; for (auto const &node: _nodes) { - if (node.predecessor != -1) { + if (node.predecessor != INVALID_NODE) { WeightType edge_weight = node.distance_to_root - _nodes[node.predecessor].distance_to_root; - edges.emplace_back(static_cast(node._id), static_cast(node.predecessor), + edges.emplace_back(static_cast(node.id), static_cast(node.predecessor), edge_weight); } } diff --git a/dijkstra/DijkstraGraph.h b/dijkstra/DijkstraGraph.h index e495bd1..b012d13 100644 --- a/dijkstra/DijkstraGraph.h +++ b/dijkstra/DijkstraGraph.h @@ -15,12 +15,12 @@ class DijkstraGraph { public: struct Node { - NodeId _id = -1; + NodeId id = INVALID_NODE; std::vector neighbours; std::vector weights; - NodeId predecessor = -1; - NodeId closest_terminal = -1; - WeightType distance_to_root = -1; + NodeId predecessor = INVALID_NODE; + NodeId closest_terminal = INVALID_NODE; + WeightType distance_to_root = INVALID_WEIGHT; }; diff --git a/graph/CoordinateGraph.cpp b/graph/CoordinateGraph.cpp index 8976ff6..a70e48a 100644 --- a/graph/CoordinateGraph.cpp +++ b/graph/CoordinateGraph.cpp @@ -3,7 +3,7 @@ // #include "CoordinateGraph.h" -#include "kruskal/disjoint_set_2.h" +#include "kruskal/DisjointSet.h" #include "STPFileParser.h" #include #include @@ -124,7 +124,7 @@ void CoordinateGraph::kruskal() { [&id_to_edge_projection](EdgeId a, EdgeId b) { return (id_to_edge_projection(a) < id_to_edge_projection(b)); }); - Disjoint_Set set; + DisjointSet set; set.make_sets(num_nodes()); for (auto edge_id: edge_ids) { auto const &edge = id_to_edge_projection(edge_id); @@ -214,24 +214,24 @@ GridUnit CoordinateGraph::mid(GridUnit a, GridUnit b, GridUnit c) { void CoordinateGraph::reduce_nodes() { NodeId orig_num_nodes = num_nodes(); - std::vector new_node_id(orig_num_nodes, -1); + std::vector new_node_id(orig_num_nodes, INVALID_NODE); std::iota(new_node_id.begin(), new_node_id.begin() + num_terminals(), 0); NodeId node_id_counter = num_terminals(); for (auto &edge: _edges) { - if (new_node_id[edge.node_a.internal_id] == -1) { + if (new_node_id[edge.node_a.internal_id] == INVALID_NODE) { new_node_id[edge.node_a.internal_id] = node_id_counter; node_id_counter += 1; } - if (new_node_id[edge.node_b.internal_id] == -1) { + if (new_node_id[edge.node_b.internal_id] == INVALID_NODE) { new_node_id[edge.node_b.internal_id] = node_id_counter; node_id_counter += 1; } edge.node_a.internal_id = new_node_id[edge.node_a.internal_id]; edge.node_b.internal_id = new_node_id[edge.node_b.internal_id]; } - std::vector new_nodes(node_id_counter, {-1, -1, -1}); + std::vector new_nodes(node_id_counter, {0, 0}); for (int i = 0; i < new_node_id.size(); i++) { - if (new_node_id[i] != -1) { + if (new_node_id[i] != INVALID_NODE) { auto const &old_node = _nodes[i]; new_nodes[new_node_id[i]] = {old_node.x_coord, old_node.y_coord, new_node_id[i]}; } diff --git a/graph/CoordinateGraph.h b/graph/CoordinateGraph.h index 056560e..656ea90 100644 --- a/graph/CoordinateGraph.h +++ b/graph/CoordinateGraph.h @@ -12,7 +12,7 @@ class CoordinateGraph { public: struct Node { - Node(GridUnit x_coord, GridUnit y_coord, NodeId internal_id = -1) + Node(GridUnit x_coord, GridUnit y_coord, NodeId internal_id = INVALID_NODE) : x_coord(x_coord), y_coord(y_coord), internal_id(internal_id) {} GridUnit x_coord; diff --git a/graph/RectilinearGraph.cpp b/graph/RectilinearGraph.cpp index 4664ba9..75c018d 100644 --- a/graph/RectilinearGraph.cpp +++ b/graph/RectilinearGraph.cpp @@ -39,11 +39,11 @@ void RectilinearGraph::add_any_edge(NodeId node_a, NodeId node_b, bool flipped) while (node_a % _grid_width < node_b % _grid_width) { _horizontal_edges_after_node[node_a] = true; - node_a += 1; + node_a++; } while (node_a % _grid_width > node_b % _grid_width) { _horizontal_edges_after_node[node_b] = true; - node_b += 1; + node_b++; } if (not flipped) { @@ -59,7 +59,8 @@ std::optional RectilinearGraph::get_horizontal_connecting_edge(NodeId no if (node_b - node_a == 1 and (node_a + 1) % _grid_width == 0) { return static_cast(node_a); - } else return std::nullopt; + } + return std::nullopt; } std::optional RectilinearGraph::get_vertical_connecting_edge(NodeId node_a, NodeId node_b) const { @@ -67,7 +68,8 @@ std::optional RectilinearGraph::get_vertical_connecting_edge(NodeId node if (node_a % _grid_width == node_b % _grid_width) { return static_cast(node_a); - } else return std::nullopt; + } + return std::nullopt; } diff --git a/kruskal/disjoint_set_2.h b/kruskal/DisjointSet.h similarity index 77% rename from kruskal/disjoint_set_2.h rename to kruskal/DisjointSet.h index 34021f1..a232edc 100644 --- a/kruskal/disjoint_set_2.h +++ b/kruskal/DisjointSet.h @@ -3,7 +3,7 @@ #include #include -class Disjoint_Set { +class DisjointSet { public: class Node { public: @@ -43,25 +43,25 @@ class Disjoint_Set { // code // } -inline Disjoint_Set::Node::Node() { +inline DisjointSet::Node::Node() { _parent = this; _depth = 0; } // getter- and setter-methods -inline void Disjoint_Set::Node::set_parent(Node *parent) { +inline void DisjointSet::Node::set_parent(Node *parent) { _parent = parent; } -inline void Disjoint_Set::Node::increment_depth() { +inline void DisjointSet::Node::increment_depth() { _depth++; } -inline Disjoint_Set::Node *Disjoint_Set::Node::get_parent() const { +inline DisjointSet::Node *DisjointSet::Node::get_parent() const { return _parent; } -inline int Disjoint_Set::Node::get_depth() const { +inline int DisjointSet::Node::get_depth() const { return _depth; } @@ -69,12 +69,12 @@ inline int Disjoint_Set::Node::get_depth() const { // the functionality of -make_set- from the lecture has been expanded to //create an arbitrary amount of sets at once. -inline void Disjoint_Set::make_sets(int num_new_sets) { +inline void DisjointSet::make_sets(int num_new_sets) { _nodes.resize(_nodes.size() + num_new_sets); } // the -find- method from the lecture -inline Disjoint_Set::Node *Disjoint_Set::find(Node *node) { +inline DisjointSet::Node *DisjointSet::find(Node *node) { Node *parent = node->get_parent(); if (parent != node) { @@ -86,7 +86,7 @@ inline Disjoint_Set::Node *Disjoint_Set::find(Node *node) { } // -union- method from the lecture (union is a keyword in c++, thus it's called unite) -inline void Disjoint_Set::unite(int a, int b) { +inline void DisjointSet::unite(int a, int b) { Node *a_ptr = &_nodes[a]; Node *b_ptr = &_nodes[b]; @@ -103,13 +103,13 @@ inline void Disjoint_Set::unite(int a, int b) { } } -inline bool Disjoint_Set::set_equals(int a, int b) { +inline bool DisjointSet::set_equals(int a, int b) { if (find(&_nodes[a]) == find(&_nodes[b])) return true; return false; } -inline int Disjoint_Set::get_num_nodes() const { +inline int DisjointSet::get_num_nodes() const { return static_cast(_nodes.size()); } diff --git a/kruskal/kruskal_ind.cpp b/kruskal/kruskal_ind.cpp index 8fd16e8..43f2256 100644 --- a/kruskal/kruskal_ind.cpp +++ b/kruskal/kruskal_ind.cpp @@ -1,7 +1,7 @@ #include "kruskal_ind.h" #include #include -#include "disjoint_set_2.h" +#include "DisjointSet.h" #include "graph/Graph.h" #include "delaunay/DelaunayGraph.h" @@ -17,7 +17,7 @@ Graph kruskal(const Graph &input_graph) { [&id_to_edge_projection](EdgeId a, EdgeId b) { return (id_to_edge_projection(a) < id_to_edge_projection(b)); }); - Disjoint_Set disjoint_set; + DisjointSet disjoint_set; disjoint_set.make_sets(return_graph.num_nodes()); for (auto edge_id: edge_ids) { auto const &edge = id_to_edge_projection(edge_id); @@ -44,7 +44,7 @@ DelaunayGraph kruskal(DelaunayGraph const &delaunay_graph) { [&id_to_edge_projection](EdgeId a, EdgeId b) { return (id_to_edge_projection(a) < id_to_edge_projection(b)); }); - Disjoint_Set set; + DisjointSet set; set.make_sets(return_graph.num_nodes()); for (auto edge_id: edge_ids) { auto const &edge = id_to_edge_projection(edge_id); diff --git a/main.cpp b/main.cpp index 1087b21..22151e3 100644 --- a/main.cpp +++ b/main.cpp @@ -23,8 +23,9 @@ int main(int argc, char *argv[]) { auto print_verbose = [& parms](std::string const &s) { - if (parms.verbose) + if (parms.verbose) { std::cout << "[Verbose]: " << s << std::endl; + } }; diff --git a/mehlhorn/MehlhornGraph.cpp b/mehlhorn/MehlhornGraph.cpp index 27582d5..ccf9b36 100644 --- a/mehlhorn/MehlhornGraph.cpp +++ b/mehlhorn/MehlhornGraph.cpp @@ -7,7 +7,7 @@ #include #include "MehlhornGraph.h" #include "dijkstra/DijkstraGraph.h" -#include "kruskal/disjoint_set_2.h" +#include "kruskal/DisjointSet.h" void MehlhornGraph::calculate_mehlhorn_graph() { DijkstraGraph dijkstra_graph(_coordinate_graph); @@ -113,7 +113,7 @@ void MehlhornGraph::kruskal_on_mehlhorn_edges() { [&id_to_edge_projection](EdgeId a, EdgeId b) { return (id_to_edge_projection(a) < id_to_edge_projection(b)); }); - Disjoint_Set disjoint_set; + DisjointSet disjoint_set; disjoint_set.make_sets(_coordinate_graph.num_nodes()); for (auto edge_id: edge_ids) { auto const &edge = id_to_edge_projection(edge_id); diff --git a/typedefs.h b/typedefs.h index ddf332c..028e673 100644 --- a/typedefs.h +++ b/typedefs.h @@ -15,6 +15,10 @@ using EdgeId = int; using GridUnit = int; using WeightType = int; +constexpr NodeId INVALID_NODE = -1; +constexpr WeightType INVALID_WEIGHT = -1; + + struct Coord { GridUnit x; GridUnit y; From ecd6d3beb2769b6dd96c4d35351ca2bdecc833ce Mon Sep 17 00:00:00 2001 From: JakobGie <44549596+armytrong@users.noreply.github.com> Date: Sat, 9 Jul 2022 10:24:35 +0200 Subject: [PATCH 25/25] undo false changes to CMakeLists.txt accidentally committed wrong changes in the commit before, removed asan and ubsan --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 22ff0ef..296fb9d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,7 @@ project(Hauptaufgabe_2) set(CMAKE_CXX_STANDARD 20) -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined -fsanitize=address -g") +#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined -fsanitize=address -g") include_directories(.) @@ -34,4 +34,4 @@ add_executable(Hauptaufgabe_2 delaunay/DelaunayPriorityQueue.cpp delaunay/DelaunayPriorityQueue.h ArgumentHandler.cpp - ArgumentHandler.h mehlhorn/MehlhornGraph.cpp mehlhorn/MehlhornGraph.h graph/CoordinateGraph.cpp graph/CoordinateGraph.h mehlhorn/mehlhorn.cpp mehlhorn/mehlhorn.h) + ArgumentHandler.h mehlhorn/MehlhornGraph.cpp mehlhorn/MehlhornGraph.h graph/CoordinateGraph.cpp graph/CoordinateGraph.h)