Skip to content

Commit

Permalink
reimplementation of optimizer
Browse files Browse the repository at this point in the history
  • Loading branch information
tanneberger committed Aug 25, 2023
1 parent baaebb7 commit 1933dcc
Show file tree
Hide file tree
Showing 4 changed files with 174 additions and 183 deletions.
5 changes: 2 additions & 3 deletions include/reactor-cpp/environment.hh
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ private:

const Duration timeout_{};

Graph<BasePort*, ConnectionProperties> graph_{};
Graph<BasePort*, ConnectionProperties> optimized_graph_{};
Graph<BasePort> graph_{};
Graph<BasePort> optimized_graph_{};

void build_dependency_graph(Reactor* reactor);
void calculate_indexes();
Expand Down Expand Up @@ -91,7 +91,6 @@ public:
}

void optimize();
void expand_and_merge();

void register_reactor(Reactor* reactor);
void register_input_action(BaseAction* action);
Expand Down
211 changes: 159 additions & 52 deletions include/reactor-cpp/graph.hh
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,34 @@
#include <iostream>
#include <map>
#include <optional>
#include <type_traits>
#include <vector>

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 E, class P> class Graph {
template <class X> class Graph {
// static_assert(std::is_base_of_v<GraphElement, X>);
using E = X*;
using P = ConnectionProperties;

private:
std::map<E, std::vector<std::pair<P, E>>> graph_;
using Path = std::vector<std::tuple<E, P, E>>;
std::map<E, std::vector<std::pair<P, E>>> graph_{};
std::set<E> nodes_{};

// custom compare operator this is required if u want special key values in std::map
Expand All @@ -39,8 +60,6 @@ public:
: graph_(std::move(graph.graph_)) {}
~Graph() noexcept = default;

using Path = std::vector<std::pair<P, E>>;

auto operator=(const Graph other) noexcept -> Graph& {
graph_ = other.graph_;
return *this;
Expand All @@ -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<std::pair<P, E>> edges;
edges.emplace_back(properties, destination);
graph_[source] = edges;
} else {
graph_[source].emplace_back(properties, destination);
Expand Down Expand Up @@ -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<std::vector<std::pair<P, E>>> {
auto naive_spanning_tree(E source) noexcept -> std::vector<std::vector<std::tuple<E, P, E>>> {
return recursive_spanning_tree(source, std::vector<E>{});
}

// this function goes recursively though the graph and tries to find every possible path
auto recursive_spanning_tree(E source_node, std::vector<E> visited_nodes)
-> std::vector<std::vector<std::pair<P, E>>> {
-> std::vector<std::vector<std::tuple<E, P, E>>> {
std::vector<Path> paths{};

if (graph_[source_node].empty()) {
return std::vector<Path>{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<Path> {
// TODO: maybe build proper djikstra here

auto spanning_tre = naive_spanning_tree(source);
std::vector<Path> 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<std::pair<P, E>> {
return graph_[source];
}

[[nodiscard]] auto get_upstream(E vertex) const noexcept -> std::optional<E> {
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<E, std::string> name_map{};
std::string mermaid_string = "graph TD;\n";

auto name_resolver = [&](E object) -> std::string {
char names[] = "ABCDEFGHIJKLMNOPQRSTUVGXYZabcdefghijklmnopqrstuvgxyz"; // NOLINT
Expand All @@ -188,6 +175,126 @@ public:
return mermaid_string;
}

void optimize(Graph<X>& optimized_graph) {
optimized_graph.clear();

static std::map<std::pair<ConnectionType, ConnectionType>, ConnectionType> construction_table = {
// Normal + x
{std::make_pair<ConnectionType, ConnectionType>(Normal, Normal), Normal},
{std::make_pair<ConnectionType, ConnectionType>(Normal, Delayed), Delayed},
{std::make_pair<ConnectionType, ConnectionType>(Normal, Enclaved), Enclaved},
{std::make_pair<ConnectionType, ConnectionType>(Normal, Physical), Physical},
{std::make_pair<ConnectionType, ConnectionType>(Normal, DelayedEnclaved), DelayedEnclaved},
{std::make_pair<ConnectionType, ConnectionType>(Normal, PhysicalEnclaved), PhysicalEnclaved},
{std::make_pair<ConnectionType, ConnectionType>(Normal, Plugin), Plugin},
// Delayed + x
{std::make_pair<ConnectionType, ConnectionType>(Delayed, Normal), Delayed},
{std::make_pair<ConnectionType, ConnectionType>(Delayed, Delayed), Delayed},
{std::make_pair<ConnectionType, ConnectionType>(Delayed, Enclaved), DelayedEnclaved},
{std::make_pair<ConnectionType, ConnectionType>(Delayed, Physical), Invalid}, //!!!
{std::make_pair<ConnectionType, ConnectionType>(Delayed, DelayedEnclaved), DelayedEnclaved},
{std::make_pair<ConnectionType, ConnectionType>(Delayed, PhysicalEnclaved), Invalid}, //!!!
{std::make_pair<ConnectionType, ConnectionType>(Delayed, Plugin), Invalid},
// Enclaved + x
{std::make_pair<ConnectionType, ConnectionType>(Enclaved, Normal), Enclaved},
{std::make_pair<ConnectionType, ConnectionType>(Enclaved, Delayed), DelayedEnclaved},
{std::make_pair<ConnectionType, ConnectionType>(Enclaved, Enclaved), Enclaved},
{std::make_pair<ConnectionType, ConnectionType>(Enclaved, Physical), PhysicalEnclaved},
{std::make_pair<ConnectionType, ConnectionType>(Enclaved, DelayedEnclaved), DelayedEnclaved},
{std::make_pair<ConnectionType, ConnectionType>(Enclaved, PhysicalEnclaved), PhysicalEnclaved},
{std::make_pair<ConnectionType, ConnectionType>(Enclaved, Plugin), Invalid},
// Physical + x
{std::make_pair<ConnectionType, ConnectionType>(Physical, Normal), Physical},
{std::make_pair<ConnectionType, ConnectionType>(Physical, Delayed), Invalid}, // !!!
{std::make_pair<ConnectionType, ConnectionType>(Physical, Enclaved), PhysicalEnclaved},
{std::make_pair<ConnectionType, ConnectionType>(Physical, Physical), Physical},
{std::make_pair<ConnectionType, ConnectionType>(Physical, DelayedEnclaved), Invalid}, // !!!
{std::make_pair<ConnectionType, ConnectionType>(Physical, PhysicalEnclaved), PhysicalEnclaved},
{std::make_pair<ConnectionType, ConnectionType>(Physical, Plugin), Invalid},
// DelayedEnclaved + x
{std::make_pair<ConnectionType, ConnectionType>(DelayedEnclaved, Normal), DelayedEnclaved},
{std::make_pair<ConnectionType, ConnectionType>(DelayedEnclaved, Delayed), DelayedEnclaved},
{std::make_pair<ConnectionType, ConnectionType>(DelayedEnclaved, Enclaved), DelayedEnclaved},
{std::make_pair<ConnectionType, ConnectionType>(DelayedEnclaved, Physical), Invalid}, // !!!
{std::make_pair<ConnectionType, ConnectionType>(DelayedEnclaved, DelayedEnclaved), DelayedEnclaved},
{std::make_pair<ConnectionType, ConnectionType>(DelayedEnclaved, PhysicalEnclaved), Invalid}, // !!!
{std::make_pair<ConnectionType, ConnectionType>(DelayedEnclaved, Plugin), Invalid},
// PhysicalEnclaved + x
{std::make_pair<ConnectionType, ConnectionType>(PhysicalEnclaved, Normal), PhysicalEnclaved},
{std::make_pair<ConnectionType, ConnectionType>(PhysicalEnclaved, Delayed), Invalid}, // !!!
{std::make_pair<ConnectionType, ConnectionType>(PhysicalEnclaved, Enclaved), PhysicalEnclaved},
{std::make_pair<ConnectionType, ConnectionType>(PhysicalEnclaved, Physical), PhysicalEnclaved},
{std::make_pair<ConnectionType, ConnectionType>(PhysicalEnclaved, DelayedEnclaved), Invalid}, // !!!
{std::make_pair<ConnectionType, ConnectionType>(PhysicalEnclaved, PhysicalEnclaved), PhysicalEnclaved},
{std::make_pair<ConnectionType, ConnectionType>(PhysicalEnclaved, Plugin), Invalid},
// Plugin + x = Invalid
{std::make_pair<ConnectionType, ConnectionType>(Plugin, Normal), Invalid}, // !!!
{std::make_pair<ConnectionType, ConnectionType>(Plugin, Delayed), Invalid}, // !!!
{std::make_pair<ConnectionType, ConnectionType>(Plugin, Enclaved), Invalid}, // !!!
{std::make_pair<ConnectionType, ConnectionType>(Plugin, Physical), Invalid}, // !!!
{std::make_pair<ConnectionType, ConnectionType>(Plugin, DelayedEnclaved), Invalid}, // !!!
{std::make_pair<ConnectionType, ConnectionType>(Plugin, PhysicalEnclaved), Invalid}, // !!!
{std::make_pair<ConnectionType, ConnectionType>(Plugin, Plugin), Invalid}, // !!!
};

// getting all the sources from the graph
auto keys = this->keys();

std::vector<E> has_downstreams{};
std::copy_if(keys.begin(), keys.end(), std::back_inserter(has_downstreams),
[](auto element) { return element->connected_to_downstream_actions(); });

std::vector<E> 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<ConnectionType, ConnectionType>(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);

// 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) {
Expand Down
14 changes: 12 additions & 2 deletions include/reactor-cpp/port.hh
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -23,7 +24,7 @@ namespace reactor {

enum class PortType { Input, Output, Delay };

class BasePort : public ReactorElement {
class BasePort : public ReactorElement, public GraphElement {
private:
BasePort* inward_binding_{nullptr};
std::set<BasePort*> outward_bindings_{};
Expand All @@ -43,6 +44,8 @@ protected:
: ReactorElement(name, match_port_enum(type), container)
, type_(type) {}

~BasePort() noexcept = default;

void register_dependency(Reaction* reaction, bool is_trigger) noexcept;
void register_antidependency(Reaction* reaction) noexcept;
virtual void cleanup() = 0;
Expand Down Expand Up @@ -93,7 +96,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_; }
Expand All @@ -103,6 +105,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 {
return has_dependencies() || has_triggers();
};
[[nodiscard]] auto connected_to_upstream_actions() const noexcept -> bool { return has_anti_dependencies(); };
[[nodiscard]] auto rating() const noexcept -> std::size_t {
return dependencies_.size() + triggers_.size() + ((set_callback_ != nullptr) ? 1 : 0);
}

void register_set_callback(const PortCallback& callback);
void register_clean_callback(const PortCallback& callback);

Expand Down
Loading

0 comments on commit 1933dcc

Please sign in to comment.