From 24bac42678ff5cbee1c07179141fcf604b6a6733 Mon Sep 17 00:00:00 2001 From: revol-xut Date: Thu, 27 Jul 2023 13:46:12 +0200 Subject: [PATCH 01/11] adding cmake flag and optimization method --- lib/CMakeLists.txt | 7 +++ lib/environment.cc | 118 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 123 insertions(+), 2 deletions(-) diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index fd47bcfb..025183f0 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -48,6 +48,13 @@ set_target_properties(${LIB_TARGET} PROPERTIES VERSION ${PROJECT_VERSION} SOVERSION 1) +option(GRAPH_OPTIMIZATIONS "Graph optimizations" OFF) +if(GRAPH_OPTIMIZATIONS) + target_compile_definitions(${LIB_TARGET} PRIVATE GRAPH_OPTIMIZATIONS=1 ) +else (GRAPH_OPTIMIZATIONS) + target_compile_definitions(${LIB_TARGET} PRIVATE GRAPH_OPTIMIZATIONS=0 ) +endif(GRAPH_OPTIMIZATIONS) + if(DEFINED LF_REACTOR_CPP_SUFFIX) install(FILES "${PROJECT_BINARY_DIR}/include/reactor-cpp/config.hh" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${LIB_TARGET}/reactor-cpp") else() diff --git a/lib/environment.cc b/lib/environment.cc index f8dc238a..499596d0 100644 --- a/lib/environment.cc +++ b/lib/environment.cc @@ -63,8 +63,122 @@ void Environment::register_input_action(BaseAction* action) { } void Environment::optimize() { - // no optimizations - optimized_graph_ = graph_; +#ifdef GRAPH_OPTIMIZATIONS + constexpr bool enable_optimizations = GRAPH_OPTIMIZATIONS; +#else + constexpr bool enable_optimizations = false; +#endif + + if constexpr (enable_optimizations) { + static std::map, ConnectionType> construction_table = { + // Normal + x + {std::make_pair(Normal, Normal), Normal}, + {std::make_pair(Normal, Delayed), Delayed}, + {std::make_pair(Normal, Enclaved), Enclaved}, + {std::make_pair(Normal, Physical), Physical}, + {std::make_pair(Normal, DelayedEnclaved), DelayedEnclaved}, + {std::make_pair(Normal, PhysicalEnclaved), PhysicalEnclaved}, + {std::make_pair(Normal, Plugin), Plugin}, + // Delayed + x + {std::make_pair(Delayed, Normal), Delayed}, + {std::make_pair(Delayed, Delayed), Delayed}, + {std::make_pair(Delayed, Enclaved), DelayedEnclaved}, + {std::make_pair(Delayed, Physical), Invalid}, //!!! + {std::make_pair(Delayed, DelayedEnclaved), DelayedEnclaved}, + {std::make_pair(Delayed, PhysicalEnclaved), Invalid}, //!!! + {std::make_pair(Delayed, Plugin), Invalid}, + // Enclaved + x + {std::make_pair(Enclaved, Normal), Enclaved}, + {std::make_pair(Enclaved, Delayed), DelayedEnclaved}, + {std::make_pair(Enclaved, Enclaved), Enclaved}, + {std::make_pair(Enclaved, Physical), PhysicalEnclaved}, + {std::make_pair(Enclaved, DelayedEnclaved), DelayedEnclaved}, + {std::make_pair(Enclaved, PhysicalEnclaved), PhysicalEnclaved}, + {std::make_pair(Enclaved, Plugin), Invalid}, + // Physical + x + {std::make_pair(Physical, Normal), Physical}, + {std::make_pair(Physical, Delayed), Invalid}, // !!! + {std::make_pair(Physical, Enclaved), PhysicalEnclaved}, + {std::make_pair(Physical, Physical), Physical}, + {std::make_pair(Physical, DelayedEnclaved), Invalid}, // !!! + {std::make_pair(Physical, PhysicalEnclaved), PhysicalEnclaved}, + {std::make_pair(Physical, Plugin), Invalid}, + // DelayedEnclaved + x + {std::make_pair(DelayedEnclaved, Normal), DelayedEnclaved}, + {std::make_pair(DelayedEnclaved, Delayed), DelayedEnclaved}, + {std::make_pair(DelayedEnclaved, Enclaved), DelayedEnclaved}, + {std::make_pair(DelayedEnclaved, Physical), Invalid}, // !!! + {std::make_pair(DelayedEnclaved, DelayedEnclaved), DelayedEnclaved}, + {std::make_pair(DelayedEnclaved, PhysicalEnclaved), Invalid}, // !!! + {std::make_pair(DelayedEnclaved, Plugin), Invalid}, + // PhysicalEnclaved + x + {std::make_pair(PhysicalEnclaved, Normal), PhysicalEnclaved}, + {std::make_pair(PhysicalEnclaved, Delayed), Invalid}, // !!! + {std::make_pair(PhysicalEnclaved, Enclaved), PhysicalEnclaved}, + {std::make_pair(PhysicalEnclaved, Physical), PhysicalEnclaved}, + {std::make_pair(PhysicalEnclaved, DelayedEnclaved), Invalid}, // !!! + {std::make_pair(PhysicalEnclaved, PhysicalEnclaved), PhysicalEnclaved}, + {std::make_pair(PhysicalEnclaved, Plugin), Invalid}, + // Plugin + x = Invalid + {std::make_pair(Plugin, Normal), Invalid}, // !!! + {std::make_pair(Plugin, Delayed), Invalid}, // !!! + {std::make_pair(Plugin, Enclaved), Invalid}, // !!! + {std::make_pair(Plugin, Physical), Invalid}, // !!! + {std::make_pair(Plugin, DelayedEnclaved), Invalid}, // !!! + {std::make_pair(Plugin, PhysicalEnclaved), Invalid}, // !!! + {std::make_pair(Plugin, Plugin), Invalid}, // !!! + }; + + // discards all current changes + optimized_graph_.clear(); + + // getting all the sources from the graph + auto keys = graph_.keys(); + + // generating all the possible destinations for all sources + for (auto* source : keys) { + auto spanning_tree = graph_.spanning_tree(source); + + for (const auto& [destination, path] : spanning_tree) { + ConnectionProperties merged_properties{}; + auto* current_source = source; + + for (auto element : path) { + auto property = element.first; + + auto return_type = + construction_table[std::pair(merged_properties.type_, property.type_)]; + + // invalid will split the connections + if (return_type == Invalid) { + // first add connection until this point + optimized_graph_.add_edge(current_source, element.second, merged_properties); + + // updating the source of the connection and resetting the properties + current_source = element.second; + merged_properties = property; + + } else { + // merging the connections + merged_properties.type_ = return_type; + + // adding up delays + merged_properties.delay_ += property.delay_; + + // updating target enclave if not nullptr + merged_properties.enclave_ = + (property.enclave_ != nullptr) ? property.enclave_ : merged_properties.enclave_; + } + } + + // add merged connection + optimized_graph_.add_edge(current_source, destination, merged_properties); + } + } + } else { + // no optimizations + optimized_graph_ = graph_; + } } void recursive_assemble(Reactor* container) { // NOLINT From 9ad576d6bdb78bea275e1c7db5ba420e9a2b0c54 Mon Sep 17 00:00:00 2001 From: revol-xut Date: Thu, 27 Jul 2023 16:38:09 +0200 Subject: [PATCH 02/11] turning optimizations of per default --- lib/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 025183f0..8b6614dd 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -48,7 +48,7 @@ set_target_properties(${LIB_TARGET} PROPERTIES VERSION ${PROJECT_VERSION} SOVERSION 1) -option(GRAPH_OPTIMIZATIONS "Graph optimizations" OFF) +option(GRAPH_OPTIMIZATIONS "Graph optimizations" ON) if(GRAPH_OPTIMIZATIONS) target_compile_definitions(${LIB_TARGET} PRIVATE GRAPH_OPTIMIZATIONS=1 ) else (GRAPH_OPTIMIZATIONS) From a1bd4061d2f30272b2f09b940dc3ab0ab4b76114 Mon Sep 17 00:00:00 2001 From: revol-xut Date: Sat, 5 Aug 2023 14:17:11 +0200 Subject: [PATCH 03/11] add basic strip method for the graph --- include/reactor-cpp/environment.hh | 2 + include/reactor-cpp/graph.hh | 116 ++++++++++--- include/reactor-cpp/port.hh | 1 + lib/CMakeLists.txt | 2 +- lib/environment.cc | 256 +++++++++++++++++------------ 5 files changed, 247 insertions(+), 130 deletions(-) diff --git a/include/reactor-cpp/environment.hh b/include/reactor-cpp/environment.hh index 1e69da88..9822af4d 100644 --- a/include/reactor-cpp/environment.hh +++ b/include/reactor-cpp/environment.hh @@ -91,6 +91,8 @@ public: } void optimize(); + void expand_and_merge(); + void strip_and_optimize(); void register_reactor(Reactor* reactor); void register_port(BasePort* port) noexcept; diff --git a/include/reactor-cpp/graph.hh b/include/reactor-cpp/graph.hh index 4009e169..f2d2ce67 100644 --- a/include/reactor-cpp/graph.hh +++ b/include/reactor-cpp/graph.hh @@ -19,6 +19,8 @@ namespace reactor { template class Graph { private: std::map>> graph_; + std::size_t index{0}; + std::map name_map{}; // custom compare operator this is required if u want special key values in std::map // this is required for the Graph::get_edges() method @@ -38,6 +40,8 @@ public: : graph_(std::move(graph.graph_)) {} ~Graph() noexcept = default; + using Path = std::vector>; + auto operator=(const Graph other) noexcept -> Graph& { graph_ = other.graph_; return *this; @@ -51,7 +55,7 @@ public: // adds a single edge to the graph structure void add_edge(E source, E destination, P properties) noexcept { if (graph_.find(source) == std::end(graph_)) { - std::vector> edges{std::make_pair(properties, destination)}; + Path edges{std::make_pair(properties, destination)}; graph_[source] = edges; } else { graph_[source].emplace_back(properties, destination); @@ -85,33 +89,88 @@ public: return keys; } - // returns the spanning tree of a given source including properties - [[nodiscard]] auto spanning_tree(E source) noexcept -> std::map>> { - std::map>> tree{}; - std::vector work_nodes{source}; - - while (!work_nodes.empty()) { - auto parent = *work_nodes.begin(); - - for (auto child : graph_[parent]) { - // figuring out the properties until this node - std::vector> parent_properties{}; - if (tree.find(parent) != std::end(tree)) { - // this if should always be the case except the first time when tree is empty - parent_properties = tree[parent]; // TODO: make sure this is a copy otherwise we change this properties as - // well - } + auto name_resolver(E object) -> std::string { + char names[] = "ABCDEFGHIJKLMNOPQRSTUVGXYZabcdefghijklmnopqrstuvgxyz"; + if(name_map.find(object) == std::end(name_map)) { + name_map[object] = names[index]; + index++; + return std::string{names[index - 1], 1}; + } + return name_map[object]; + } + + // the return type looks a little bit cursed what is happening here ? + // we have a map from the destination as a key to a list of paths through the graph. + // A path here is modelled by a list of edges (with properties and the next vertex). + auto naive_spanning_tree(E source) noexcept -> std::vector>> { + auto result = recursive_spanning_tree(source, std::vector{}); + + std::string mermaid_string = "graph TD;\n"; + + for (auto path : result ) { + mermaid_string += std::string(" ") + name_resolver(source); + for (auto edge : path) { + mermaid_string += std::string("-->")+ name_resolver(edge.second); + } + mermaid_string += std::string(";\n"); + + } + //std::cout << "EDGE: \n" << mermaid_string << std::endl; + return result; + } + + + // this function goes recursively though the graph and tries to find every possible path + auto recursive_spanning_tree(E source_node, std::vector visited_nodes) -> std::vector>> { + std::vector paths{}; + + if (graph_[source_node].empty()) { + return std::vector{Path{}}; + } + + for (auto child : graph_[source_node]) { + E current_node = child.second; + + // means this node has not been visited yey + if (std::find(std::begin(visited_nodes), std::end(visited_nodes), current_node) == std::end(visited_nodes)) { - // appending the new property and inserting into the tree - parent_properties.push_back(child); - work_nodes.push_back(child.second); - tree[child.second] = parent_properties; + // creating a temporary vector where the currently selected vertex is appended + auto temp_nodes = visited_nodes; + temp_nodes.push_back(current_node); + + for (auto path: recursive_spanning_tree(current_node, temp_nodes)) { + path.insert(std::begin(path), child); + paths.push_back(path); + } } + } + + return paths; + } - work_nodes.erase(std::begin(work_nodes)); + auto shortest_path(E source, E destination) -> std::optional { + // TODO: maybe build proper djikstra here + + auto spanning_tre = naive_spanning_tree(source); + std::vector relevant_paths{}; + + std::copy_if(spanning_tre.begin(), spanning_tre.end(), std::back_inserter(relevant_paths), [destination](Path path) { + return (*path.end()).second == destination; + }); + + if (relevant_paths.empty()) { + return std::nullopt; } - return tree; + Path best_path = *relevant_paths.begin(); + + for (auto path : relevant_paths) { + if (path.size() < best_path.size()) { + best_path = path; + } + } + + return best_path; } [[nodiscard]] auto get_destinations(E source) const noexcept -> std::vector> { @@ -126,6 +185,17 @@ public: } } + auto to_mermaid() noexcept -> std::string { + std::string mermaid_string = "graph TD;\n"; + + for (const auto& [source, destinations] : graph_) { + for (auto dest : destinations) { + mermaid_string += std::string(" ") + name_resolver(source) + std::string("-->") + name_resolver(dest.second) + std::string(";\n"); + } + } + return mermaid_string; + } + friend auto operator<<(std::ostream& outstream, const Graph& graph) -> std::ostream& { for (auto const& [source, destinations] : graph.graph_) { for (auto destination : destinations) { diff --git a/include/reactor-cpp/port.hh b/include/reactor-cpp/port.hh index 6fe01494..e7561310 100644 --- a/include/reactor-cpp/port.hh +++ b/include/reactor-cpp/port.hh @@ -92,6 +92,7 @@ public: [[nodiscard]] inline auto has_outward_bindings() const noexcept -> bool { return !outward_bindings_.empty(); } [[nodiscard]] inline auto has_dependencies() const noexcept -> bool { return !dependencies_.empty(); } [[nodiscard]] inline auto has_anti_dependencies() const noexcept -> bool { return !anti_dependencies_.empty(); } + [[nodiscard]] inline auto has_triggers() const noexcept -> bool { return !triggers_.empty(); } [[nodiscard]] inline auto inward_binding() const noexcept -> BasePort* { return inward_binding_; } [[nodiscard]] inline auto outward_bindings() const noexcept -> const auto& { return outward_bindings_; } diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 8b6614dd..febe0c0d 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -22,7 +22,7 @@ else() set(REACTOR_CPP_INCLUDE "include") endif() -add_library(${LIB_TARGET} SHARED ${SOURCE_FILES}) +add_library(${LIB_TARGET} STATIC ${SOURCE_FILES}) target_include_directories(${LIB_TARGET} PUBLIC "$" "$" diff --git a/lib/environment.cc b/lib/environment.cc index 499596d0..024d1188 100644 --- a/lib/environment.cc +++ b/lib/environment.cc @@ -64,123 +64,167 @@ void Environment::register_input_action(BaseAction* action) { void Environment::optimize() { #ifdef GRAPH_OPTIMIZATIONS - constexpr bool enable_optimizations = GRAPH_OPTIMIZATIONS; + constexpr bool enable_optimizations = true; #else constexpr bool enable_optimizations = false; #endif - + std::cout << "OPTIMIZATIONS: " << enable_optimizations << std::endl; if constexpr (enable_optimizations) { - static std::map, ConnectionType> construction_table = { - // Normal + x - {std::make_pair(Normal, Normal), Normal}, - {std::make_pair(Normal, Delayed), Delayed}, - {std::make_pair(Normal, Enclaved), Enclaved}, - {std::make_pair(Normal, Physical), Physical}, - {std::make_pair(Normal, DelayedEnclaved), DelayedEnclaved}, - {std::make_pair(Normal, PhysicalEnclaved), PhysicalEnclaved}, - {std::make_pair(Normal, Plugin), Plugin}, - // Delayed + x - {std::make_pair(Delayed, Normal), Delayed}, - {std::make_pair(Delayed, Delayed), Delayed}, - {std::make_pair(Delayed, Enclaved), DelayedEnclaved}, - {std::make_pair(Delayed, Physical), Invalid}, //!!! - {std::make_pair(Delayed, DelayedEnclaved), DelayedEnclaved}, - {std::make_pair(Delayed, PhysicalEnclaved), Invalid}, //!!! - {std::make_pair(Delayed, Plugin), Invalid}, - // Enclaved + x - {std::make_pair(Enclaved, Normal), Enclaved}, - {std::make_pair(Enclaved, Delayed), DelayedEnclaved}, - {std::make_pair(Enclaved, Enclaved), Enclaved}, - {std::make_pair(Enclaved, Physical), PhysicalEnclaved}, - {std::make_pair(Enclaved, DelayedEnclaved), DelayedEnclaved}, - {std::make_pair(Enclaved, PhysicalEnclaved), PhysicalEnclaved}, - {std::make_pair(Enclaved, Plugin), Invalid}, - // Physical + x - {std::make_pair(Physical, Normal), Physical}, - {std::make_pair(Physical, Delayed), Invalid}, // !!! - {std::make_pair(Physical, Enclaved), PhysicalEnclaved}, - {std::make_pair(Physical, Physical), Physical}, - {std::make_pair(Physical, DelayedEnclaved), Invalid}, // !!! - {std::make_pair(Physical, PhysicalEnclaved), PhysicalEnclaved}, - {std::make_pair(Physical, Plugin), Invalid}, - // DelayedEnclaved + x - {std::make_pair(DelayedEnclaved, Normal), DelayedEnclaved}, - {std::make_pair(DelayedEnclaved, Delayed), DelayedEnclaved}, - {std::make_pair(DelayedEnclaved, Enclaved), DelayedEnclaved}, - {std::make_pair(DelayedEnclaved, Physical), Invalid}, // !!! - {std::make_pair(DelayedEnclaved, DelayedEnclaved), DelayedEnclaved}, - {std::make_pair(DelayedEnclaved, PhysicalEnclaved), Invalid}, // !!! - {std::make_pair(DelayedEnclaved, Plugin), Invalid}, - // PhysicalEnclaved + x - {std::make_pair(PhysicalEnclaved, Normal), PhysicalEnclaved}, - {std::make_pair(PhysicalEnclaved, Delayed), Invalid}, // !!! - {std::make_pair(PhysicalEnclaved, Enclaved), PhysicalEnclaved}, - {std::make_pair(PhysicalEnclaved, Physical), PhysicalEnclaved}, - {std::make_pair(PhysicalEnclaved, DelayedEnclaved), Invalid}, // !!! - {std::make_pair(PhysicalEnclaved, PhysicalEnclaved), PhysicalEnclaved}, - {std::make_pair(PhysicalEnclaved, Plugin), Invalid}, - // Plugin + x = Invalid - {std::make_pair(Plugin, Normal), Invalid}, // !!! - {std::make_pair(Plugin, Delayed), Invalid}, // !!! - {std::make_pair(Plugin, Enclaved), Invalid}, // !!! - {std::make_pair(Plugin, Physical), Invalid}, // !!! - {std::make_pair(Plugin, DelayedEnclaved), Invalid}, // !!! - {std::make_pair(Plugin, PhysicalEnclaved), Invalid}, // !!! - {std::make_pair(Plugin, Plugin), Invalid}, // !!! - }; - - // discards all current changes - optimized_graph_.clear(); - - // getting all the sources from the graph - auto keys = graph_.keys(); - - // generating all the possible destinations for all sources - for (auto* source : keys) { - auto spanning_tree = graph_.spanning_tree(source); - - for (const auto& [destination, path] : spanning_tree) { - ConnectionProperties merged_properties{}; - auto* current_source = source; - - for (auto element : path) { - auto property = element.first; - - auto return_type = - construction_table[std::pair(merged_properties.type_, property.type_)]; - - // invalid will split the connections - if (return_type == Invalid) { - // first add connection until this point - optimized_graph_.add_edge(current_source, element.second, merged_properties); - - // updating the source of the connection and resetting the properties - current_source = element.second; - merged_properties = property; - - } else { - // merging the connections - merged_properties.type_ = return_type; - - // adding up delays - merged_properties.delay_ += property.delay_; - - // updating target enclave if not nullptr - merged_properties.enclave_ = - (property.enclave_ != nullptr) ? property.enclave_ : merged_properties.enclave_; - } - } + std::cout << graph_.to_mermaid() << std::endl; + expand_and_merge(); + strip_and_optimize(); - // add merged connection - optimized_graph_.add_edge(current_source, destination, merged_properties); - } - } + std::cout << optimized_graph_.to_mermaid() << std::endl; } else { // no optimizations optimized_graph_ = graph_; } } +void Environment::expand_and_merge() { + + static std::map, ConnectionType> construction_table = { + // Normal + x + {std::make_pair(Normal, Normal), Normal}, + {std::make_pair(Normal, Delayed), Delayed}, + {std::make_pair(Normal, Enclaved), Enclaved}, + {std::make_pair(Normal, Physical), Physical}, + {std::make_pair(Normal, DelayedEnclaved), DelayedEnclaved}, + {std::make_pair(Normal, PhysicalEnclaved), PhysicalEnclaved}, + {std::make_pair(Normal, Plugin), Plugin}, + // Delayed + x + {std::make_pair(Delayed, Normal), Delayed}, + {std::make_pair(Delayed, Delayed), Delayed}, + {std::make_pair(Delayed, Enclaved), DelayedEnclaved}, + {std::make_pair(Delayed, Physical), Invalid}, //!!! + {std::make_pair(Delayed, DelayedEnclaved), DelayedEnclaved}, + {std::make_pair(Delayed, PhysicalEnclaved), Invalid}, //!!! + {std::make_pair(Delayed, Plugin), Invalid}, + // Enclaved + x + {std::make_pair(Enclaved, Normal), Enclaved}, + {std::make_pair(Enclaved, Delayed), DelayedEnclaved}, + {std::make_pair(Enclaved, Enclaved), Enclaved}, + {std::make_pair(Enclaved, Physical), PhysicalEnclaved}, + {std::make_pair(Enclaved, DelayedEnclaved), DelayedEnclaved}, + {std::make_pair(Enclaved, PhysicalEnclaved), PhysicalEnclaved}, + {std::make_pair(Enclaved, Plugin), Invalid}, + // Physical + x + {std::make_pair(Physical, Normal), Physical}, + {std::make_pair(Physical, Delayed), Invalid}, // !!! + {std::make_pair(Physical, Enclaved), PhysicalEnclaved}, + {std::make_pair(Physical, Physical), Physical}, + {std::make_pair(Physical, DelayedEnclaved), Invalid}, // !!! + {std::make_pair(Physical, PhysicalEnclaved), PhysicalEnclaved}, + {std::make_pair(Physical, Plugin), Invalid}, + // DelayedEnclaved + x + {std::make_pair(DelayedEnclaved, Normal), DelayedEnclaved}, + {std::make_pair(DelayedEnclaved, Delayed), DelayedEnclaved}, + {std::make_pair(DelayedEnclaved, Enclaved), DelayedEnclaved}, + {std::make_pair(DelayedEnclaved, Physical), Invalid}, // !!! + {std::make_pair(DelayedEnclaved, DelayedEnclaved), DelayedEnclaved}, + {std::make_pair(DelayedEnclaved, PhysicalEnclaved), Invalid}, // !!! + {std::make_pair(DelayedEnclaved, Plugin), Invalid}, + // PhysicalEnclaved + x + {std::make_pair(PhysicalEnclaved, Normal), PhysicalEnclaved}, + {std::make_pair(PhysicalEnclaved, Delayed), Invalid}, // !!! + {std::make_pair(PhysicalEnclaved, Enclaved), PhysicalEnclaved}, + {std::make_pair(PhysicalEnclaved, Physical), PhysicalEnclaved}, + {std::make_pair(PhysicalEnclaved, DelayedEnclaved), Invalid}, // !!! + {std::make_pair(PhysicalEnclaved, PhysicalEnclaved), PhysicalEnclaved}, + {std::make_pair(PhysicalEnclaved, Plugin), Invalid}, + // Plugin + x = Invalid + {std::make_pair(Plugin, Normal), Invalid}, // !!! + {std::make_pair(Plugin, Delayed), Invalid}, // !!! + {std::make_pair(Plugin, Enclaved), Invalid}, // !!! + {std::make_pair(Plugin, Physical), Invalid}, // !!! + {std::make_pair(Plugin, DelayedEnclaved), Invalid}, // !!! + {std::make_pair(Plugin, PhysicalEnclaved), Invalid}, // !!! + {std::make_pair(Plugin, Plugin), Invalid}, // !!! + }; + + // discards all current changes + optimized_graph_.clear(); + + // getting all the sources from the graph + auto keys = graph_.keys(); + + // generating all the possible destinations for all sources + for (auto* source : keys) { + auto spanning_tree = graph_.naive_spanning_tree(source); + for (auto& path : spanning_tree) { + ConnectionProperties merged_properties{}; + auto* current_source = source; + + std::cout << std::string(" ") << graph_.name_resolver(source); + for (auto element : path) { + std::cout << std::string("-->") << graph_.name_resolver(element.second); + auto property = element.first; + + auto return_type = + construction_table[std::pair(merged_properties.type_, property.type_)]; + + // invalid will split the connections + if (return_type == Invalid) { + // first add connection until this point + optimized_graph_.add_edge(current_source, element.second, merged_properties); + + // updating the source of the connection and resetting the properties + current_source = element.second; + merged_properties = property; + + } else { + // merging the connections + merged_properties.type_ = return_type; + + // adding up delays + merged_properties.delay_ += property.delay_; + + // updating target enclave if not nullptr + merged_properties.enclave_ = + (property.enclave_ != nullptr) ? property.enclave_ : merged_properties.enclave_; + + optimized_graph_.add_edge(current_source, element.second, merged_properties); + } + } + std::cout << std::endl; + } + } +} + +void Environment::strip_and_optimize() { + Graph striped_graph{}; + + auto nodes = optimized_graph_.keys(); + std::vector has_downstream_reactions{}; + std::copy_if(nodes.begin(), nodes.end(), std::back_inserter(has_downstream_reactions), [](BasePort* port) { + return port->has_anti_dependencies(); + }); + + std::vector has_triggers{}; + std::copy_if(nodes.begin(), nodes.end(), std::back_inserter(has_triggers), [](BasePort* port) { + return port->has_dependencies(); + }); + + for (auto downstream : has_downstream_reactions) { + for (auto upstream : has_triggers) { + if (upstream != downstream) { + auto optional_path = optimized_graph_.shortest_path(downstream, upstream); + + if (optional_path.has_value()) { + auto best_path = optional_path.value(); + auto source = downstream; + for (auto hop : best_path) { + striped_graph.add_edge(source, hop.second, hop.first); + source = hop.second; + } + } + } + } + } + + optimized_graph_ = striped_graph; +} + void recursive_assemble(Reactor* container) { // NOLINT container->assemble(); for (auto* reactor : container->reactors()) { From 30158d4ef350391a2ef3130f473edb92e92febe2 Mon Sep 17 00:00:00 2001 From: revol-xut Date: Sat, 5 Aug 2023 15:00:39 +0200 Subject: [PATCH 04/11] very stupid shortest path algorithm --- include/reactor-cpp/graph.hh | 60 ++++++++++++++++-------------------- lib/CMakeLists.txt | 2 +- lib/environment.cc | 22 ++++--------- 3 files changed, 33 insertions(+), 51 deletions(-) diff --git a/include/reactor-cpp/graph.hh b/include/reactor-cpp/graph.hh index f2d2ce67..87447c2c 100644 --- a/include/reactor-cpp/graph.hh +++ b/include/reactor-cpp/graph.hh @@ -19,8 +19,7 @@ namespace reactor { template class Graph { private: std::map>> graph_; - std::size_t index{0}; - std::map name_map{}; + std::set nodes_{}; // custom compare operator this is required if u want special key values in std::map // this is required for the Graph::get_edges() method @@ -54,6 +53,8 @@ public: // adds a single edge to the graph structure void add_edge(E source, E destination, P properties) noexcept { + nodes_.insert(source); + nodes_.insert(destination); if (graph_.find(source) == std::end(graph_)) { Path edges{std::make_pair(properties, destination)}; graph_[source] = edges; @@ -62,6 +63,8 @@ public: } } + auto get_nodes() -> std::set { return nodes_; } + // this groups connections by same source and properties [[nodiscard]] auto get_edges() const noexcept -> std::map, map_key_compare> { std::map, map_key_compare> all_edges{}; @@ -89,39 +92,16 @@ public: return keys; } - auto name_resolver(E object) -> std::string { - char names[] = "ABCDEFGHIJKLMNOPQRSTUVGXYZabcdefghijklmnopqrstuvgxyz"; - if(name_map.find(object) == std::end(name_map)) { - name_map[object] = names[index]; - index++; - return std::string{names[index - 1], 1}; - } - return name_map[object]; - } - // the return type looks a little bit cursed what is happening here ? // we have a map from the destination as a key to a list of paths through the graph. // A path here is modelled by a list of edges (with properties and the next vertex). auto naive_spanning_tree(E source) noexcept -> std::vector>> { - auto result = recursive_spanning_tree(source, std::vector{}); - - std::string mermaid_string = "graph TD;\n"; - - for (auto path : result ) { - mermaid_string += std::string(" ") + name_resolver(source); - for (auto edge : path) { - mermaid_string += std::string("-->")+ name_resolver(edge.second); - } - mermaid_string += std::string(";\n"); - - } - //std::cout << "EDGE: \n" << mermaid_string << std::endl; - return result; + return recursive_spanning_tree(source, std::vector{}); } - // this function goes recursively though the graph and tries to find every possible path - auto recursive_spanning_tree(E source_node, std::vector visited_nodes) -> std::vector>> { + auto recursive_spanning_tree(E source_node, std::vector visited_nodes) + -> std::vector>> { std::vector paths{}; if (graph_[source_node].empty()) { @@ -138,7 +118,7 @@ public: auto temp_nodes = visited_nodes; temp_nodes.push_back(current_node); - for (auto path: recursive_spanning_tree(current_node, temp_nodes)) { + for (auto path : recursive_spanning_tree(current_node, temp_nodes)) { path.insert(std::begin(path), child); paths.push_back(path); } @@ -154,9 +134,8 @@ public: auto spanning_tre = naive_spanning_tree(source); std::vector relevant_paths{}; - std::copy_if(spanning_tre.begin(), spanning_tre.end(), std::back_inserter(relevant_paths), [destination](Path path) { - return (*path.end()).second == destination; - }); + std::copy_if(spanning_tre.begin(), spanning_tre.end(), std::back_inserter(relevant_paths), + [&, destination](Path path) { return path[path.size() - 1].second == destination; }); if (relevant_paths.empty()) { return std::nullopt; @@ -185,12 +164,25 @@ public: } } - auto to_mermaid() noexcept -> std::string { + auto to_mermaid() const noexcept -> std::string { + std::size_t index{0}; + std::map name_map{}; std::string mermaid_string = "graph TD;\n"; + auto name_resolver = [&](E object) -> std::string { + char names[] = "ABCDEFGHIJKLMNOPQRSTUVGXYZabcdefghijklmnopqrstuvgxyz"; + if (name_map.find(object) == std::end(name_map)) { + name_map[object] = names[index]; + index++; + return std::string{names[index - 1], 1}; + } + return name_map[object]; + }; + for (const auto& [source, destinations] : graph_) { for (auto dest : destinations) { - mermaid_string += std::string(" ") + name_resolver(source) + std::string("-->") + name_resolver(dest.second) + std::string(";\n"); + mermaid_string += std::string(" ") + name_resolver(source) + std::string("-->") + + name_resolver(dest.second) + std::string(";\n"); } } return mermaid_string; diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index febe0c0d..8b6614dd 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -22,7 +22,7 @@ else() set(REACTOR_CPP_INCLUDE "include") endif() -add_library(${LIB_TARGET} STATIC ${SOURCE_FILES}) +add_library(${LIB_TARGET} SHARED ${SOURCE_FILES}) target_include_directories(${LIB_TARGET} PUBLIC "$" "$" diff --git a/lib/environment.cc b/lib/environment.cc index 024d1188..9143ef7c 100644 --- a/lib/environment.cc +++ b/lib/environment.cc @@ -68,13 +68,9 @@ void Environment::optimize() { #else constexpr bool enable_optimizations = false; #endif - std::cout << "OPTIMIZATIONS: " << enable_optimizations << std::endl; if constexpr (enable_optimizations) { - std::cout << graph_.to_mermaid() << std::endl; expand_and_merge(); strip_and_optimize(); - - std::cout << optimized_graph_.to_mermaid() << std::endl; } else { // no optimizations optimized_graph_ = graph_; @@ -155,9 +151,7 @@ void Environment::expand_and_merge() { ConnectionProperties merged_properties{}; auto* current_source = source; - std::cout << std::string(" ") << graph_.name_resolver(source); for (auto element : path) { - std::cout << std::string("-->") << graph_.name_resolver(element.second); auto property = element.first; auto return_type = @@ -180,13 +174,11 @@ void Environment::expand_and_merge() { merged_properties.delay_ += property.delay_; // updating target enclave if not nullptr - merged_properties.enclave_ = - (property.enclave_ != nullptr) ? property.enclave_ : merged_properties.enclave_; + merged_properties.enclave_ = (property.enclave_ != nullptr) ? property.enclave_ : merged_properties.enclave_; optimized_graph_.add_edge(current_source, element.second, merged_properties); } } - std::cout << std::endl; } } } @@ -194,16 +186,14 @@ void Environment::expand_and_merge() { void Environment::strip_and_optimize() { Graph striped_graph{}; - auto nodes = optimized_graph_.keys(); + auto nodes = optimized_graph_.get_nodes(); std::vector has_downstream_reactions{}; - std::copy_if(nodes.begin(), nodes.end(), std::back_inserter(has_downstream_reactions), [](BasePort* port) { - return port->has_anti_dependencies(); - }); + std::copy_if(nodes.begin(), nodes.end(), std::back_inserter(has_downstream_reactions), + [](BasePort* port) { return port->has_anti_dependencies(); }); std::vector has_triggers{}; - std::copy_if(nodes.begin(), nodes.end(), std::back_inserter(has_triggers), [](BasePort* port) { - return port->has_dependencies(); - }); + std::copy_if(nodes.begin(), nodes.end(), std::back_inserter(has_triggers), + [](BasePort* port) { return port->has_dependencies(); }); for (auto downstream : has_downstream_reactions) { for (auto upstream : has_triggers) { From 044d29119a03d88e42faef78c37569d2334dcbdf Mon Sep 17 00:00:00 2001 From: revol-xut Date: Mon, 21 Aug 2023 15:31:48 +0200 Subject: [PATCH 05/11] fixed ordering problem in assemble --- include/reactor-cpp/environment.hh | 2 - include/reactor-cpp/graph.hh | 4 +- include/reactor-cpp/port.hh | 1 + lib/environment.cc | 106 +++++++++++++---------------- lib/port.cc | 2 + 5 files changed, 51 insertions(+), 64 deletions(-) diff --git a/include/reactor-cpp/environment.hh b/include/reactor-cpp/environment.hh index 9822af4d..a30bb8cd 100644 --- a/include/reactor-cpp/environment.hh +++ b/include/reactor-cpp/environment.hh @@ -92,10 +92,8 @@ public: void optimize(); void expand_and_merge(); - void strip_and_optimize(); void register_reactor(Reactor* reactor); - void register_port(BasePort* port) noexcept; void register_input_action(BaseAction* action); void assemble(); auto startup() -> std::thread; diff --git a/include/reactor-cpp/graph.hh b/include/reactor-cpp/graph.hh index 87447c2c..52bff0d0 100644 --- a/include/reactor-cpp/graph.hh +++ b/include/reactor-cpp/graph.hh @@ -164,13 +164,13 @@ public: } } - auto to_mermaid() const noexcept -> std::string { + [[nodiscard]] auto to_mermaid() const noexcept -> std::string { std::size_t index{0}; std::map name_map{}; std::string mermaid_string = "graph TD;\n"; auto name_resolver = [&](E object) -> std::string { - char names[] = "ABCDEFGHIJKLMNOPQRSTUVGXYZabcdefghijklmnopqrstuvgxyz"; + char names[] = "ABCDEFGHIJKLMNOPQRSTUVGXYZabcdefghijklmnopqrstuvgxyz"; //NOLINT if (name_map.find(object) == std::end(name_map)) { name_map[object] = names[index]; index++; diff --git a/include/reactor-cpp/port.hh b/include/reactor-cpp/port.hh index e7561310..ec2b750b 100644 --- a/include/reactor-cpp/port.hh +++ b/include/reactor-cpp/port.hh @@ -93,6 +93,7 @@ public: [[nodiscard]] inline auto has_dependencies() const noexcept -> bool { return !dependencies_.empty(); } [[nodiscard]] inline auto has_anti_dependencies() const noexcept -> bool { return !anti_dependencies_.empty(); } [[nodiscard]] inline auto has_triggers() const noexcept -> bool { return !triggers_.empty(); } + [[nodiscard]] inline auto rating() const noexcept -> std::size_t { return triggers_.size() + dependencies_.size(); } [[nodiscard]] inline auto inward_binding() const noexcept -> BasePort* { return inward_binding_; } [[nodiscard]] inline auto outward_bindings() const noexcept -> const auto& { return outward_bindings_; } diff --git a/lib/environment.cc b/lib/environment.cc index 9143ef7c..4c9ce621 100644 --- a/lib/environment.cc +++ b/lib/environment.cc @@ -63,14 +63,16 @@ void Environment::register_input_action(BaseAction* action) { } void Environment::optimize() { -#ifdef GRAPH_OPTIMIZATIONS +#if GRAPH_OPTIMIZATIONS constexpr bool enable_optimizations = true; #else constexpr bool enable_optimizations = false; #endif + log::Debug() << "Opimizations:" << enable_optimizations; if constexpr (enable_optimizations) { + log::Debug() << graph_.to_mermaid(); expand_and_merge(); - strip_and_optimize(); + log::Debug() << optimized_graph_.to_mermaid(); } else { // no optimizations optimized_graph_ = graph_; @@ -149,70 +151,53 @@ void Environment::expand_and_merge() { auto spanning_tree = graph_.naive_spanning_tree(source); for (auto& path : spanning_tree) { ConnectionProperties merged_properties{}; - auto* current_source = source; - for (auto element : path) { - auto property = element.first; + std::reverse(path.begin(), path.end()); - auto return_type = - construction_table[std::pair(merged_properties.type_, property.type_)]; + auto* previous_element = std::begin(path)->second; + for (auto it = std::begin(path); it != std::end(path); ++it) { + if (std::next(it) == std::end(path)) { + it->second = source; + } else { + it->second = std::next(it)->second; + } + } - // invalid will split the connections - if (return_type == Invalid) { - // first add connection until this point - optimized_graph_.add_edge(current_source, element.second, merged_properties); + auto current_rating = previous_element->rating(); - // updating the source of the connection and resetting the properties - current_source = element.second; - merged_properties = property; + for (auto element : path) { + auto property = element.first; + current_rating += element.second->rating(); - } else { - // merging the connections - merged_properties.type_ = return_type; + if (current_rating > 0) { + auto return_type = + construction_table[std::pair(merged_properties.type_, property.type_)]; + // invalid will split the connections + if (return_type == Invalid) { + // first add connection until this point + optimized_graph_.add_edge(element.second, previous_element, merged_properties); - // adding up delays - merged_properties.delay_ += property.delay_; + // updating the source of the connection and resetting the properties + previous_element = element.second; + merged_properties = property; - // updating target enclave if not nullptr - merged_properties.enclave_ = (property.enclave_ != nullptr) ? property.enclave_ : merged_properties.enclave_; + } else { + // merging the connections + merged_properties.type_ = return_type; - optimized_graph_.add_edge(current_source, element.second, merged_properties); - } - } - } - } -} + // adding up delays + merged_properties.delay_ += property.delay_; -void Environment::strip_and_optimize() { - Graph striped_graph{}; - - auto nodes = optimized_graph_.get_nodes(); - std::vector has_downstream_reactions{}; - std::copy_if(nodes.begin(), nodes.end(), std::back_inserter(has_downstream_reactions), - [](BasePort* port) { return port->has_anti_dependencies(); }); - - std::vector has_triggers{}; - std::copy_if(nodes.begin(), nodes.end(), std::back_inserter(has_triggers), - [](BasePort* port) { return port->has_dependencies(); }); - - for (auto downstream : has_downstream_reactions) { - for (auto upstream : has_triggers) { - if (upstream != downstream) { - auto optional_path = optimized_graph_.shortest_path(downstream, upstream); - - if (optional_path.has_value()) { - auto best_path = optional_path.value(); - auto source = downstream; - for (auto hop : best_path) { - striped_graph.add_edge(source, hop.second, hop.first); - source = hop.second; + // updating target enclave if not nullptr + merged_properties.enclave_ = + (property.enclave_ != nullptr) ? property.enclave_ : merged_properties.enclave_; + + optimized_graph_.add_edge(element.second, previous_element, merged_properties); } } } } } - - optimized_graph_ = striped_graph; } void recursive_assemble(Reactor* container) { // NOLINT @@ -233,11 +218,17 @@ void Environment::assemble() { // NOLINT recursive_assemble(reactor); } - log::Debug() << "start optimization on port graph"; - this->optimize(); + // this assembles all the contained environments aka enclaves + for (auto* env : contained_environments_) { + env->assemble(); + } - log::Debug() << "instantiating port graph declaration"; if (top_environment_ == nullptr || top_environment_ == this) { + log::Debug() << "start optimization on port graph"; + this->optimize(); + + log::Debug() << "instantiating port graph declaration"; + log::Debug() << "graph: "; log::Debug() << optimized_graph_; @@ -291,11 +282,6 @@ void Environment::assemble() { // NOLINT } calculate_indexes(); - - // this assembles all the contained environments aka enclaves - for (auto* env : contained_environments_) { - env->assemble(); - } } void Environment::build_dependency_graph(Reactor* reactor) { // NOLINT diff --git a/lib/port.cc b/lib/port.cc index 8525df66..401f9828 100644 --- a/lib/port.cc +++ b/lib/port.cc @@ -30,6 +30,8 @@ void BasePort::register_dependency(Reaction* reaction, bool is_trigger) noexcept "Dependent output ports must belong to a contained reactor"); } + log::Debug() << "registering dependency for : " << this->fqn() << " with reaction: " << reaction->fqn() + << " is trigger: " << is_trigger; [[maybe_unused]] bool result = dependencies_.insert(reaction).second; reactor_assert(result); if (is_trigger) { From baaebb7190bd4432f43ddac62d00ae7946db8103 Mon Sep 17 00:00:00 2001 From: revol-xut Date: Mon, 21 Aug 2023 15:45:09 +0200 Subject: [PATCH 06/11] making clang tidy happy --- include/reactor-cpp/graph.hh | 2 +- lib/environment.cc | 18 +++++++++++------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/include/reactor-cpp/graph.hh b/include/reactor-cpp/graph.hh index 52bff0d0..45bd455c 100644 --- a/include/reactor-cpp/graph.hh +++ b/include/reactor-cpp/graph.hh @@ -170,7 +170,7 @@ public: std::string mermaid_string = "graph TD;\n"; auto name_resolver = [&](E object) -> std::string { - char names[] = "ABCDEFGHIJKLMNOPQRSTUVGXYZabcdefghijklmnopqrstuvgxyz"; //NOLINT + char names[] = "ABCDEFGHIJKLMNOPQRSTUVGXYZabcdefghijklmnopqrstuvgxyz"; // NOLINT if (name_map.find(object) == std::end(name_map)) { name_map[object] = names[index]; index++; diff --git a/lib/environment.cc b/lib/environment.cc index 4c9ce621..77103066 100644 --- a/lib/environment.cc +++ b/lib/environment.cc @@ -24,6 +24,16 @@ namespace reactor { +void vector_shuffle(std::vector>& path, BasePort* source) { + for (auto it = std::begin(path); it != std::end(path); ++it) { + if (std::next(it) == std::end(path)) { + it->second = source; + } else { + it->second = std::next(it)->second; + } + } +}; + Environment::Environment(unsigned int num_workers, bool fast_fwd_execution, const Duration& timeout) : log_("Environment") , num_workers_(num_workers) @@ -155,13 +165,7 @@ void Environment::expand_and_merge() { std::reverse(path.begin(), path.end()); auto* previous_element = std::begin(path)->second; - for (auto it = std::begin(path); it != std::end(path); ++it) { - if (std::next(it) == std::end(path)) { - it->second = source; - } else { - it->second = std::next(it)->second; - } - } + vector_shuffle(path, source); auto current_rating = previous_element->rating(); From 4f3da1c7c4c840c80beb937c8253b5f75160658f Mon Sep 17 00:00:00 2001 From: revol-xut Date: Fri, 25 Aug 2023 07:49:13 +0200 Subject: [PATCH 07/11] reimplementation of optimizer --- include/reactor-cpp/environment.hh | 5 +- include/reactor-cpp/graph.hh | 211 ++++++++++++++++++++++------- include/reactor-cpp/port.hh | 21 ++- lib/environment.cc | 127 +---------------- 4 files changed, 177 insertions(+), 187 deletions(-) diff --git a/include/reactor-cpp/environment.hh b/include/reactor-cpp/environment.hh index a30bb8cd..ad8d67bd 100644 --- a/include/reactor-cpp/environment.hh +++ b/include/reactor-cpp/environment.hh @@ -59,8 +59,8 @@ private: const Duration timeout_{}; - Graph graph_{}; - Graph optimized_graph_{}; + Graph graph_{}; + Graph optimized_graph_{}; void build_dependency_graph(Reactor* reactor); void calculate_indexes(); @@ -91,7 +91,6 @@ public: } void optimize(); - void expand_and_merge(); void register_reactor(Reactor* reactor); void register_input_action(BaseAction* action); diff --git a/include/reactor-cpp/graph.hh b/include/reactor-cpp/graph.hh index 45bd455c..cc7a0c8e 100644 --- a/include/reactor-cpp/graph.hh +++ b/include/reactor-cpp/graph.hh @@ -12,13 +12,34 @@ #include #include #include +#include #include namespace reactor { +class GraphElement { +public: + GraphElement() noexcept = default; + GraphElement(const GraphElement& graph) noexcept = default; + GraphElement(GraphElement&& graph) noexcept = default; + + virtual ~GraphElement() noexcept = default; + [[nodiscard]] virtual auto connected_to_downstream_actions() const noexcept -> bool = 0; + [[nodiscard]] virtual auto connected_to_upstream_actions() const noexcept -> bool = 0; + [[nodiscard]] virtual auto rating() const noexcept -> std::size_t = 0; + + auto operator=([[maybe_unused]] const GraphElement& other) noexcept -> GraphElement& = default; + auto operator=([[maybe_unused]] GraphElement&& other) noexcept -> GraphElement& = default; +}; + // this graph is special, because to every edge properties are annotated -template class Graph { +template class Graph { + // static_assert(std::is_base_of_v); + using E = X*; + using P = ConnectionProperties; + private: - std::map>> graph_; + using Path = std::vector>; + std::map>> graph_{}; std::set nodes_{}; // custom compare operator this is required if u want special key values in std::map @@ -39,8 +60,6 @@ public: : graph_(std::move(graph.graph_)) {} ~Graph() noexcept = default; - using Path = std::vector>; - auto operator=(const Graph other) noexcept -> Graph& { graph_ = other.graph_; return *this; @@ -55,8 +74,10 @@ public: void add_edge(E source, E destination, P properties) noexcept { nodes_.insert(source); nodes_.insert(destination); + if (graph_.find(source) == std::end(graph_)) { - Path edges{std::make_pair(properties, destination)}; + std::vector> edges; + edges.emplace_back(properties, destination); graph_[source] = edges; } else { graph_[source].emplace_back(properties, destination); @@ -95,79 +116,45 @@ public: // the return type looks a little bit cursed what is happening here ? // we have a map from the destination as a key to a list of paths through the graph. // A path here is modelled by a list of edges (with properties and the next vertex). - auto naive_spanning_tree(E source) noexcept -> std::vector>> { + auto naive_spanning_tree(E source) noexcept -> std::vector>> { return recursive_spanning_tree(source, std::vector{}); } // this function goes recursively though the graph and tries to find every possible path auto recursive_spanning_tree(E source_node, std::vector visited_nodes) - -> std::vector>> { + -> std::vector>> { std::vector paths{}; if (graph_[source_node].empty()) { return std::vector{Path{}}; } + // if this node has an action we need to append the path + if (source_node->connected_to_downstream_actions()) { + paths.push_back(Path{}); + } + for (auto child : graph_[source_node]) { E current_node = child.second; - // means this node has not been visited yey - if (std::find(std::begin(visited_nodes), std::end(visited_nodes), current_node) == std::end(visited_nodes)) { - - // creating a temporary vector where the currently selected vertex is appended - auto temp_nodes = visited_nodes; - temp_nodes.push_back(current_node); - - for (auto path : recursive_spanning_tree(current_node, temp_nodes)) { - path.insert(std::begin(path), child); - paths.push_back(path); - } + // we dont need to check for cycles because lf semantics assure that there wont be any cycles + for (auto path : recursive_spanning_tree(current_node, visited_nodes)) { + path.push_back(std::make_tuple(source_node, child.first, current_node)); + paths.push_back(path); } } return paths; } - auto shortest_path(E source, E destination) -> std::optional { - // TODO: maybe build proper djikstra here - - auto spanning_tre = naive_spanning_tree(source); - std::vector relevant_paths{}; - - std::copy_if(spanning_tre.begin(), spanning_tre.end(), std::back_inserter(relevant_paths), - [&, destination](Path path) { return path[path.size() - 1].second == destination; }); - - if (relevant_paths.empty()) { - return std::nullopt; - } - - Path best_path = *relevant_paths.begin(); - - for (auto path : relevant_paths) { - if (path.size() < best_path.size()) { - best_path = path; - } - } - - return best_path; - } - [[nodiscard]] auto get_destinations(E source) const noexcept -> std::vector> { - return graph_[source]; - } - - [[nodiscard]] auto get_upstream(E vertex) const noexcept -> std::optional { - for (const auto& [source, sinks] : graph_) { - if (sinks.second.contains(vertex)) { - return source; - } - } + return this->graph_.at(source); } [[nodiscard]] auto to_mermaid() const noexcept -> std::string { + std::string mermaid_string = "graph TD;\n"; std::size_t index{0}; std::map name_map{}; - std::string mermaid_string = "graph TD;\n"; auto name_resolver = [&](E object) -> std::string { char names[] = "ABCDEFGHIJKLMNOPQRSTUVGXYZabcdefghijklmnopqrstuvgxyz"; // NOLINT @@ -188,6 +175,126 @@ public: return mermaid_string; } + void optimize(Graph& optimized_graph) { + optimized_graph.clear(); + + static std::map, ConnectionType> construction_table = { + // Normal + x + {std::make_pair(Normal, Normal), Normal}, + {std::make_pair(Normal, Delayed), Delayed}, + {std::make_pair(Normal, Enclaved), Enclaved}, + {std::make_pair(Normal, Physical), Physical}, + {std::make_pair(Normal, DelayedEnclaved), DelayedEnclaved}, + {std::make_pair(Normal, PhysicalEnclaved), PhysicalEnclaved}, + {std::make_pair(Normal, Plugin), Plugin}, + // Delayed + x + {std::make_pair(Delayed, Normal), Delayed}, + {std::make_pair(Delayed, Delayed), Delayed}, + {std::make_pair(Delayed, Enclaved), DelayedEnclaved}, + {std::make_pair(Delayed, Physical), Invalid}, //!!! + {std::make_pair(Delayed, DelayedEnclaved), DelayedEnclaved}, + {std::make_pair(Delayed, PhysicalEnclaved), Invalid}, //!!! + {std::make_pair(Delayed, Plugin), Invalid}, + // Enclaved + x + {std::make_pair(Enclaved, Normal), Enclaved}, + {std::make_pair(Enclaved, Delayed), DelayedEnclaved}, + {std::make_pair(Enclaved, Enclaved), Enclaved}, + {std::make_pair(Enclaved, Physical), PhysicalEnclaved}, + {std::make_pair(Enclaved, DelayedEnclaved), DelayedEnclaved}, + {std::make_pair(Enclaved, PhysicalEnclaved), PhysicalEnclaved}, + {std::make_pair(Enclaved, Plugin), Invalid}, + // Physical + x + {std::make_pair(Physical, Normal), Physical}, + {std::make_pair(Physical, Delayed), Invalid}, // !!! + {std::make_pair(Physical, Enclaved), PhysicalEnclaved}, + {std::make_pair(Physical, Physical), Physical}, + {std::make_pair(Physical, DelayedEnclaved), Invalid}, // !!! + {std::make_pair(Physical, PhysicalEnclaved), PhysicalEnclaved}, + {std::make_pair(Physical, Plugin), Invalid}, + // DelayedEnclaved + x + {std::make_pair(DelayedEnclaved, Normal), DelayedEnclaved}, + {std::make_pair(DelayedEnclaved, Delayed), DelayedEnclaved}, + {std::make_pair(DelayedEnclaved, Enclaved), DelayedEnclaved}, + {std::make_pair(DelayedEnclaved, Physical), Invalid}, // !!! + {std::make_pair(DelayedEnclaved, DelayedEnclaved), DelayedEnclaved}, + {std::make_pair(DelayedEnclaved, PhysicalEnclaved), Invalid}, // !!! + {std::make_pair(DelayedEnclaved, Plugin), Invalid}, + // PhysicalEnclaved + x + {std::make_pair(PhysicalEnclaved, Normal), PhysicalEnclaved}, + {std::make_pair(PhysicalEnclaved, Delayed), Invalid}, // !!! + {std::make_pair(PhysicalEnclaved, Enclaved), PhysicalEnclaved}, + {std::make_pair(PhysicalEnclaved, Physical), PhysicalEnclaved}, + {std::make_pair(PhysicalEnclaved, DelayedEnclaved), Invalid}, // !!! + {std::make_pair(PhysicalEnclaved, PhysicalEnclaved), PhysicalEnclaved}, + {std::make_pair(PhysicalEnclaved, Plugin), Invalid}, + // Plugin + x = Invalid + {std::make_pair(Plugin, Normal), Invalid}, // !!! + {std::make_pair(Plugin, Delayed), Invalid}, // !!! + {std::make_pair(Plugin, Enclaved), Invalid}, // !!! + {std::make_pair(Plugin, Physical), Invalid}, // !!! + {std::make_pair(Plugin, DelayedEnclaved), Invalid}, // !!! + {std::make_pair(Plugin, PhysicalEnclaved), Invalid}, // !!! + {std::make_pair(Plugin, Plugin), Invalid}, // !!! + }; + + // getting all the sources from the graph + auto keys = this->keys(); + + std::vector has_downstreams{}; + std::copy_if(keys.begin(), keys.end(), std::back_inserter(has_downstreams), + [](auto element) { return element->connected_to_downstream_actions(); }); + + std::vector has_upstreams{}; + std::copy_if(keys.begin(), keys.end(), std::back_inserter(has_upstreams), + [](auto element) { return element->connected_to_upstream_actions(); }); + + // generating all the possible destinations for all sources + for (auto* source : has_upstreams) { + auto spanning_tree = naive_spanning_tree(source); + + for (auto& path : spanning_tree) { + ConnectionProperties merged_properties{}; + auto* final_destination = std::get<2>(*std::begin(path)); + std::size_t current_rating = 0; + + for (auto edge : path) { + auto property = std::get<1>(edge); + // auto source_port = std::get<0>(edge); + auto* destination_port = std::get<2>(edge); + + current_rating += destination_port->rating(); + + if (current_rating > 0) { + auto return_type = + construction_table[std::pair(merged_properties.type_, property.type_)]; + // invalid will split the connections + if (return_type == Invalid) { + // first add connection until this point + optimized_graph.add_edge(destination_port, final_destination, merged_properties); // NOLINT + + // resetting the properties and destination_port + final_destination = destination_port; + merged_properties = property; + + } else { + + // merging the connections + merged_properties.type_ = return_type; + + // adding up delays + merged_properties.delay_ += property.delay_; + + // updating target enclave if not nullptr + merged_properties.enclave_ = + (property.enclave_ != nullptr) ? property.enclave_ : merged_properties.enclave_; + } + } + } + optimized_graph.add_edge(std::get<0>(*(std::end(path) - 1)), final_destination, merged_properties); + } + } + } + friend auto operator<<(std::ostream& outstream, const Graph& graph) -> std::ostream& { for (auto const& [source, destinations] : graph.graph_) { for (auto destination : destinations) { diff --git a/include/reactor-cpp/port.hh b/include/reactor-cpp/port.hh index ec2b750b..745306ec 100644 --- a/include/reactor-cpp/port.hh +++ b/include/reactor-cpp/port.hh @@ -15,6 +15,7 @@ #include "assert.hh" #include "connection_properties.hh" #include "fwd.hh" +#include "graph.hh" #include "multiport.hh" #include "reactor_element.hh" #include "value_ptr.hh" @@ -23,7 +24,7 @@ namespace reactor { enum class PortType { Input, Output, Delay }; -class BasePort : public ReactorElement { +class BasePort : public ReactorElement, public GraphElement { // NOLINT private: BasePort* inward_binding_{nullptr}; std::set outward_bindings_{}; @@ -39,10 +40,6 @@ private: protected: bool present_{false}; // NOLINT cppcoreguidelines-non-private-member-variables-in-classes - BasePort(const std::string& name, PortType type, Reactor* container) - : ReactorElement(name, match_port_enum(type), container) - , type_(type) {} - void register_dependency(Reaction* reaction, bool is_trigger) noexcept; void register_antidependency(Reaction* reaction) noexcept; virtual void cleanup() = 0; @@ -71,6 +68,11 @@ protected: } public: + BasePort(const std::string& name, PortType type, Reactor* container) + : ReactorElement(name, match_port_enum(type), container) + , type_(type) {} + ~BasePort() noexcept override = default; + void set_inward_binding(BasePort* port) noexcept { inward_binding_ = port; } void add_outward_binding(BasePort* port) noexcept { outward_bindings_.insert(port); // NOLINT @@ -93,7 +95,6 @@ public: [[nodiscard]] inline auto has_dependencies() const noexcept -> bool { return !dependencies_.empty(); } [[nodiscard]] inline auto has_anti_dependencies() const noexcept -> bool { return !anti_dependencies_.empty(); } [[nodiscard]] inline auto has_triggers() const noexcept -> bool { return !triggers_.empty(); } - [[nodiscard]] inline auto rating() const noexcept -> std::size_t { return triggers_.size() + dependencies_.size(); } [[nodiscard]] inline auto inward_binding() const noexcept -> BasePort* { return inward_binding_; } [[nodiscard]] inline auto outward_bindings() const noexcept -> const auto& { return outward_bindings_; } @@ -103,6 +104,14 @@ public: [[nodiscard]] inline auto anti_dependencies() const noexcept -> const auto& { return anti_dependencies_; } [[nodiscard]] inline auto port_type() const noexcept -> PortType { return type_; } + [[nodiscard]] auto connected_to_downstream_actions() const noexcept -> bool final { + return has_dependencies() || has_triggers(); + }; + [[nodiscard]] auto connected_to_upstream_actions() const noexcept -> bool final { return has_anti_dependencies(); }; + [[nodiscard]] auto rating() const noexcept -> std::size_t final { + return dependencies_.size() + triggers_.size() + ((set_callback_ != nullptr) ? 1 : 0); + } + void register_set_callback(const PortCallback& callback); void register_clean_callback(const PortCallback& callback); diff --git a/lib/environment.cc b/lib/environment.cc index 77103066..ae154a70 100644 --- a/lib/environment.cc +++ b/lib/environment.cc @@ -24,16 +24,6 @@ namespace reactor { -void vector_shuffle(std::vector>& path, BasePort* source) { - for (auto it = std::begin(path); it != std::end(path); ++it) { - if (std::next(it) == std::end(path)) { - it->second = source; - } else { - it->second = std::next(it)->second; - } - } -}; - Environment::Environment(unsigned int num_workers, bool fast_fwd_execution, const Duration& timeout) : log_("Environment") , num_workers_(num_workers) @@ -81,7 +71,7 @@ void Environment::optimize() { log::Debug() << "Opimizations:" << enable_optimizations; if constexpr (enable_optimizations) { log::Debug() << graph_.to_mermaid(); - expand_and_merge(); + graph_.optimize(optimized_graph_); log::Debug() << optimized_graph_.to_mermaid(); } else { // no optimizations @@ -89,121 +79,6 @@ void Environment::optimize() { } } -void Environment::expand_and_merge() { - - static std::map, ConnectionType> construction_table = { - // Normal + x - {std::make_pair(Normal, Normal), Normal}, - {std::make_pair(Normal, Delayed), Delayed}, - {std::make_pair(Normal, Enclaved), Enclaved}, - {std::make_pair(Normal, Physical), Physical}, - {std::make_pair(Normal, DelayedEnclaved), DelayedEnclaved}, - {std::make_pair(Normal, PhysicalEnclaved), PhysicalEnclaved}, - {std::make_pair(Normal, Plugin), Plugin}, - // Delayed + x - {std::make_pair(Delayed, Normal), Delayed}, - {std::make_pair(Delayed, Delayed), Delayed}, - {std::make_pair(Delayed, Enclaved), DelayedEnclaved}, - {std::make_pair(Delayed, Physical), Invalid}, //!!! - {std::make_pair(Delayed, DelayedEnclaved), DelayedEnclaved}, - {std::make_pair(Delayed, PhysicalEnclaved), Invalid}, //!!! - {std::make_pair(Delayed, Plugin), Invalid}, - // Enclaved + x - {std::make_pair(Enclaved, Normal), Enclaved}, - {std::make_pair(Enclaved, Delayed), DelayedEnclaved}, - {std::make_pair(Enclaved, Enclaved), Enclaved}, - {std::make_pair(Enclaved, Physical), PhysicalEnclaved}, - {std::make_pair(Enclaved, DelayedEnclaved), DelayedEnclaved}, - {std::make_pair(Enclaved, PhysicalEnclaved), PhysicalEnclaved}, - {std::make_pair(Enclaved, Plugin), Invalid}, - // Physical + x - {std::make_pair(Physical, Normal), Physical}, - {std::make_pair(Physical, Delayed), Invalid}, // !!! - {std::make_pair(Physical, Enclaved), PhysicalEnclaved}, - {std::make_pair(Physical, Physical), Physical}, - {std::make_pair(Physical, DelayedEnclaved), Invalid}, // !!! - {std::make_pair(Physical, PhysicalEnclaved), PhysicalEnclaved}, - {std::make_pair(Physical, Plugin), Invalid}, - // DelayedEnclaved + x - {std::make_pair(DelayedEnclaved, Normal), DelayedEnclaved}, - {std::make_pair(DelayedEnclaved, Delayed), DelayedEnclaved}, - {std::make_pair(DelayedEnclaved, Enclaved), DelayedEnclaved}, - {std::make_pair(DelayedEnclaved, Physical), Invalid}, // !!! - {std::make_pair(DelayedEnclaved, DelayedEnclaved), DelayedEnclaved}, - {std::make_pair(DelayedEnclaved, PhysicalEnclaved), Invalid}, // !!! - {std::make_pair(DelayedEnclaved, Plugin), Invalid}, - // PhysicalEnclaved + x - {std::make_pair(PhysicalEnclaved, Normal), PhysicalEnclaved}, - {std::make_pair(PhysicalEnclaved, Delayed), Invalid}, // !!! - {std::make_pair(PhysicalEnclaved, Enclaved), PhysicalEnclaved}, - {std::make_pair(PhysicalEnclaved, Physical), PhysicalEnclaved}, - {std::make_pair(PhysicalEnclaved, DelayedEnclaved), Invalid}, // !!! - {std::make_pair(PhysicalEnclaved, PhysicalEnclaved), PhysicalEnclaved}, - {std::make_pair(PhysicalEnclaved, Plugin), Invalid}, - // Plugin + x = Invalid - {std::make_pair(Plugin, Normal), Invalid}, // !!! - {std::make_pair(Plugin, Delayed), Invalid}, // !!! - {std::make_pair(Plugin, Enclaved), Invalid}, // !!! - {std::make_pair(Plugin, Physical), Invalid}, // !!! - {std::make_pair(Plugin, DelayedEnclaved), Invalid}, // !!! - {std::make_pair(Plugin, PhysicalEnclaved), Invalid}, // !!! - {std::make_pair(Plugin, Plugin), Invalid}, // !!! - }; - - // discards all current changes - optimized_graph_.clear(); - - // getting all the sources from the graph - auto keys = graph_.keys(); - - // generating all the possible destinations for all sources - for (auto* source : keys) { - auto spanning_tree = graph_.naive_spanning_tree(source); - for (auto& path : spanning_tree) { - ConnectionProperties merged_properties{}; - - std::reverse(path.begin(), path.end()); - - auto* previous_element = std::begin(path)->second; - vector_shuffle(path, source); - - auto current_rating = previous_element->rating(); - - for (auto element : path) { - auto property = element.first; - current_rating += element.second->rating(); - - if (current_rating > 0) { - auto return_type = - construction_table[std::pair(merged_properties.type_, property.type_)]; - // invalid will split the connections - if (return_type == Invalid) { - // first add connection until this point - optimized_graph_.add_edge(element.second, previous_element, merged_properties); - - // updating the source of the connection and resetting the properties - previous_element = element.second; - merged_properties = property; - - } else { - // merging the connections - merged_properties.type_ = return_type; - - // adding up delays - merged_properties.delay_ += property.delay_; - - // updating target enclave if not nullptr - merged_properties.enclave_ = - (property.enclave_ != nullptr) ? property.enclave_ : merged_properties.enclave_; - - optimized_graph_.add_edge(element.second, previous_element, merged_properties); - } - } - } - } - } -} - void recursive_assemble(Reactor* container) { // NOLINT container->assemble(); for (auto* reactor : container->reactors()) { From 470f22543f5e13aaf5a4b4101560deb652558c21 Mon Sep 17 00:00:00 2001 From: revol-xut Date: Thu, 31 Aug 2023 14:04:33 +0200 Subject: [PATCH 08/11] fiddeling around with constructors --- include/reactor-cpp/port.hh | 13 +++++++------ include/reactor-cpp/reactor_element.hh | 2 +- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/include/reactor-cpp/port.hh b/include/reactor-cpp/port.hh index 745306ec..8999a876 100644 --- a/include/reactor-cpp/port.hh +++ b/include/reactor-cpp/port.hh @@ -24,7 +24,7 @@ namespace reactor { enum class PortType { Input, Output, Delay }; -class BasePort : public ReactorElement, public GraphElement { // NOLINT +class BasePort : public GraphElement, public ReactorElement { // NOLINT private: BasePort* inward_binding_{nullptr}; std::set outward_bindings_{}; @@ -40,6 +40,10 @@ private: protected: bool present_{false}; // NOLINT cppcoreguidelines-non-private-member-variables-in-classes + BasePort(const std::string& name, PortType type, Reactor* container) + : ReactorElement(name, match_port_enum(type), container) + , type_(type) {} + void register_dependency(Reaction* reaction, bool is_trigger) noexcept; void register_antidependency(Reaction* reaction) noexcept; virtual void cleanup() = 0; @@ -68,9 +72,6 @@ protected: } public: - BasePort(const std::string& name, PortType type, Reactor* container) - : ReactorElement(name, match_port_enum(type), container) - , type_(type) {} ~BasePort() noexcept override = default; void set_inward_binding(BasePort* port) noexcept { inward_binding_ = port; } @@ -181,7 +182,7 @@ public: Input(const std::string& name, Reactor* container) : Port(name, PortType::Input, container) {} - Input(Input&&) = default; // NOLINT(performance-noexcept-move-constructor) + Input(Input&&) noexcept = default; // NOLINT(performance-noexcept-move-constructor) }; template class Output : public Port { // NOLINT @@ -189,7 +190,7 @@ public: Output(const std::string& name, Reactor* container) : Port(name, PortType::Output, container) {} - Output(Output&&) = default; // NOLINT(performance-noexcept-move-constructor) + Output(Output&&) noexcept = default; // NOLINT(performance-noexcept-move-constructor) }; } // namespace reactor diff --git a/include/reactor-cpp/reactor_element.hh b/include/reactor-cpp/reactor_element.hh index f6322058..94bbbbaa 100644 --- a/include/reactor-cpp/reactor_element.hh +++ b/include/reactor-cpp/reactor_element.hh @@ -37,7 +37,7 @@ public: virtual ~ReactorElement() = default; // not copyable, but movable - ReactorElement(const ReactorElement&) = delete; + ReactorElement(const ReactorElement&) = default; ReactorElement(ReactorElement&&) = default; [[nodiscard]] auto container() const noexcept -> Reactor* { return container_; } From a030d10e98502090a3027380ca712faec8e37ea3 Mon Sep 17 00:00:00 2001 From: revol-xut Date: Thu, 31 Aug 2023 14:14:05 +0200 Subject: [PATCH 09/11] use static_cast instead of reinterpret_cast --- lib/reactor_element.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/reactor_element.cc b/lib/reactor_element.cc index db111566..9fd1d5f0 100644 --- a/lib/reactor_element.cc +++ b/lib/reactor_element.cc @@ -39,10 +39,10 @@ ReactorElement::ReactorElement(const std::string& name, ReactorElement::Type typ container->register_action(reinterpret_cast(this)); // NOLINT break; case Type::Input: - container->register_input(reinterpret_cast(this)); // NOLINT + container->register_input(static_cast(this)); // NOLINT break; case Type::Output: - container->register_output(reinterpret_cast(this)); // NOLINT + container->register_output(static_cast(this)); // NOLINT break; case Type::Reaction: container->register_reaction(reinterpret_cast(this)); // NOLINT From e592f2779b56e466a99c7d0ef580f68a03f3c74f Mon Sep 17 00:00:00 2001 From: revol-xut Date: Thu, 31 Aug 2023 16:34:16 +0200 Subject: [PATCH 10/11] smal check to avoid segfaults --- include/reactor-cpp/graph.hh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/reactor-cpp/graph.hh b/include/reactor-cpp/graph.hh index cc7a0c8e..54b8034b 100644 --- a/include/reactor-cpp/graph.hh +++ b/include/reactor-cpp/graph.hh @@ -253,6 +253,10 @@ public: auto spanning_tree = naive_spanning_tree(source); for (auto& path : spanning_tree) { + if (path.empty()) { + continue; + } + ConnectionProperties merged_properties{}; auto* final_destination = std::get<2>(*std::begin(path)); std::size_t current_rating = 0; From fd8fda0a58b99482e4c8808344ebc3b79a2b13de Mon Sep 17 00:00:00 2001 From: revol-xut Date: Mon, 18 Dec 2023 13:49:14 +0100 Subject: [PATCH 11/11] including algorithm.h --- include/reactor-cpp/graph.hh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) mode change 100644 => 100755 include/reactor-cpp/graph.hh diff --git a/include/reactor-cpp/graph.hh b/include/reactor-cpp/graph.hh old mode 100644 new mode 100755 index 54b8034b..7786dfed --- a/include/reactor-cpp/graph.hh +++ b/include/reactor-cpp/graph.hh @@ -9,6 +9,7 @@ #ifndef REACTOR_CPP_GRAPH_HH #define REACTOR_CPP_GRAPH_HH +#include #include #include #include @@ -309,4 +310,4 @@ public: } }; } // namespace reactor -#endif // REACTOR_CPP_GRAPH_HH \ No newline at end of file +#endif // REACTOR_CPP_GRAPH_HH