Skip to content

Commit 50958e6

Browse files
committed
reimplementation of optimizer
1 parent baaebb7 commit 50958e6

File tree

4 files changed

+174
-183
lines changed

4 files changed

+174
-183
lines changed

include/reactor-cpp/environment.hh

+2-3
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,8 @@ private:
5959

6060
const Duration timeout_{};
6161

62-
Graph<BasePort*, ConnectionProperties> graph_{};
63-
Graph<BasePort*, ConnectionProperties> optimized_graph_{};
62+
Graph<BasePort> graph_{};
63+
Graph<BasePort> optimized_graph_{};
6464

6565
void build_dependency_graph(Reactor* reactor);
6666
void calculate_indexes();
@@ -91,7 +91,6 @@ public:
9191
}
9292

9393
void optimize();
94-
void expand_and_merge();
9594

9695
void register_reactor(Reactor* reactor);
9796
void register_input_action(BaseAction* action);

include/reactor-cpp/graph.hh

+159-52
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,34 @@
1212
#include <iostream>
1313
#include <map>
1414
#include <optional>
15+
#include <type_traits>
1516
#include <vector>
1617

1718
namespace reactor {
19+
class GraphElement {
20+
public:
21+
GraphElement() noexcept = default;
22+
GraphElement(const GraphElement& graph) noexcept = default;
23+
GraphElement(GraphElement&& graph) noexcept = default;
24+
25+
virtual ~GraphElement() noexcept = default;
26+
[[nodiscard]] virtual auto connected_to_downstream_actions() const noexcept -> bool = 0;
27+
[[nodiscard]] virtual auto connected_to_upstream_actions() const noexcept -> bool = 0;
28+
[[nodiscard]] virtual auto rating() const noexcept -> std::size_t = 0;
29+
30+
auto operator=([[maybe_unused]] const GraphElement& other) noexcept -> GraphElement& = default;
31+
auto operator=([[maybe_unused]] GraphElement&& other) noexcept -> GraphElement& = default;
32+
};
33+
1834
// this graph is special, because to every edge properties are annotated
19-
template <class E, class P> class Graph {
35+
template <class X> class Graph {
36+
// static_assert(std::is_base_of_v<GraphElement, X>);
37+
using E = X*;
38+
using P = ConnectionProperties;
39+
2040
private:
21-
std::map<E, std::vector<std::pair<P, E>>> graph_;
41+
using Path = std::vector<std::tuple<E, P, E>>;
42+
std::map<E, std::vector<std::pair<P, E>>> graph_{};
2243
std::set<E> nodes_{};
2344

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

42-
using Path = std::vector<std::pair<P, E>>;
43-
4463
auto operator=(const Graph other) noexcept -> Graph& {
4564
graph_ = other.graph_;
4665
return *this;
@@ -55,8 +74,10 @@ public:
5574
void add_edge(E source, E destination, P properties) noexcept {
5675
nodes_.insert(source);
5776
nodes_.insert(destination);
77+
5878
if (graph_.find(source) == std::end(graph_)) {
59-
Path edges{std::make_pair(properties, destination)};
79+
std::vector<std::pair<P, E>> edges;
80+
edges.emplace_back(properties, destination);
6081
graph_[source] = edges;
6182
} else {
6283
graph_[source].emplace_back(properties, destination);
@@ -95,79 +116,45 @@ public:
95116
// the return type looks a little bit cursed what is happening here ?
96117
// we have a map from the destination as a key to a list of paths through the graph.
97118
// A path here is modelled by a list of edges (with properties and the next vertex).
98-
auto naive_spanning_tree(E source) noexcept -> std::vector<std::vector<std::pair<P, E>>> {
119+
auto naive_spanning_tree(E source) noexcept -> std::vector<std::vector<std::tuple<E, P, E>>> {
99120
return recursive_spanning_tree(source, std::vector<E>{});
100121
}
101122

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

107128
if (graph_[source_node].empty()) {
108129
return std::vector<Path>{Path{}};
109130
}
110131

132+
// if this node has an action we need to append the path
133+
if (source_node->connected_to_downstream_actions()) {
134+
paths.push_back(Path{});
135+
}
136+
111137
for (auto child : graph_[source_node]) {
112138
E current_node = child.second;
113139

114-
// means this node has not been visited yey
115-
if (std::find(std::begin(visited_nodes), std::end(visited_nodes), current_node) == std::end(visited_nodes)) {
116-
117-
// creating a temporary vector where the currently selected vertex is appended
118-
auto temp_nodes = visited_nodes;
119-
temp_nodes.push_back(current_node);
120-
121-
for (auto path : recursive_spanning_tree(current_node, temp_nodes)) {
122-
path.insert(std::begin(path), child);
123-
paths.push_back(path);
124-
}
140+
// we dont need to check for cycles because lf semantics assure that there wont be any cycles
141+
for (auto path : recursive_spanning_tree(current_node, visited_nodes)) {
142+
path.push_back(std::make_tuple(source_node, child.first, current_node));
143+
paths.push_back(path);
125144
}
126145
}
127146

128147
return paths;
129148
}
130149

131-
auto shortest_path(E source, E destination) -> std::optional<Path> {
132-
// TODO: maybe build proper djikstra here
133-
134-
auto spanning_tre = naive_spanning_tree(source);
135-
std::vector<Path> relevant_paths{};
136-
137-
std::copy_if(spanning_tre.begin(), spanning_tre.end(), std::back_inserter(relevant_paths),
138-
[&, destination](Path path) { return path[path.size() - 1].second == destination; });
139-
140-
if (relevant_paths.empty()) {
141-
return std::nullopt;
142-
}
143-
144-
Path best_path = *relevant_paths.begin();
145-
146-
for (auto path : relevant_paths) {
147-
if (path.size() < best_path.size()) {
148-
best_path = path;
149-
}
150-
}
151-
152-
return best_path;
153-
}
154-
155150
[[nodiscard]] auto get_destinations(E source) const noexcept -> std::vector<std::pair<P, E>> {
156-
return graph_[source];
157-
}
158-
159-
[[nodiscard]] auto get_upstream(E vertex) const noexcept -> std::optional<E> {
160-
for (const auto& [source, sinks] : graph_) {
161-
if (sinks.second.contains(vertex)) {
162-
return source;
163-
}
164-
}
151+
return this->graph_.at(source);
165152
}
166153

167154
[[nodiscard]] auto to_mermaid() const noexcept -> std::string {
155+
std::string mermaid_string = "graph TD;\n";
168156
std::size_t index{0};
169157
std::map<E, std::string> name_map{};
170-
std::string mermaid_string = "graph TD;\n";
171158

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

178+
void optimize(Graph<X>& optimized_graph) {
179+
optimized_graph.clear();
180+
181+
static std::map<std::pair<ConnectionType, ConnectionType>, ConnectionType> construction_table = {
182+
// Normal + x
183+
{std::make_pair<ConnectionType, ConnectionType>(Normal, Normal), Normal},
184+
{std::make_pair<ConnectionType, ConnectionType>(Normal, Delayed), Delayed},
185+
{std::make_pair<ConnectionType, ConnectionType>(Normal, Enclaved), Enclaved},
186+
{std::make_pair<ConnectionType, ConnectionType>(Normal, Physical), Physical},
187+
{std::make_pair<ConnectionType, ConnectionType>(Normal, DelayedEnclaved), DelayedEnclaved},
188+
{std::make_pair<ConnectionType, ConnectionType>(Normal, PhysicalEnclaved), PhysicalEnclaved},
189+
{std::make_pair<ConnectionType, ConnectionType>(Normal, Plugin), Plugin},
190+
// Delayed + x
191+
{std::make_pair<ConnectionType, ConnectionType>(Delayed, Normal), Delayed},
192+
{std::make_pair<ConnectionType, ConnectionType>(Delayed, Delayed), Delayed},
193+
{std::make_pair<ConnectionType, ConnectionType>(Delayed, Enclaved), DelayedEnclaved},
194+
{std::make_pair<ConnectionType, ConnectionType>(Delayed, Physical), Invalid}, //!!!
195+
{std::make_pair<ConnectionType, ConnectionType>(Delayed, DelayedEnclaved), DelayedEnclaved},
196+
{std::make_pair<ConnectionType, ConnectionType>(Delayed, PhysicalEnclaved), Invalid}, //!!!
197+
{std::make_pair<ConnectionType, ConnectionType>(Delayed, Plugin), Invalid},
198+
// Enclaved + x
199+
{std::make_pair<ConnectionType, ConnectionType>(Enclaved, Normal), Enclaved},
200+
{std::make_pair<ConnectionType, ConnectionType>(Enclaved, Delayed), DelayedEnclaved},
201+
{std::make_pair<ConnectionType, ConnectionType>(Enclaved, Enclaved), Enclaved},
202+
{std::make_pair<ConnectionType, ConnectionType>(Enclaved, Physical), PhysicalEnclaved},
203+
{std::make_pair<ConnectionType, ConnectionType>(Enclaved, DelayedEnclaved), DelayedEnclaved},
204+
{std::make_pair<ConnectionType, ConnectionType>(Enclaved, PhysicalEnclaved), PhysicalEnclaved},
205+
{std::make_pair<ConnectionType, ConnectionType>(Enclaved, Plugin), Invalid},
206+
// Physical + x
207+
{std::make_pair<ConnectionType, ConnectionType>(Physical, Normal), Physical},
208+
{std::make_pair<ConnectionType, ConnectionType>(Physical, Delayed), Invalid}, // !!!
209+
{std::make_pair<ConnectionType, ConnectionType>(Physical, Enclaved), PhysicalEnclaved},
210+
{std::make_pair<ConnectionType, ConnectionType>(Physical, Physical), Physical},
211+
{std::make_pair<ConnectionType, ConnectionType>(Physical, DelayedEnclaved), Invalid}, // !!!
212+
{std::make_pair<ConnectionType, ConnectionType>(Physical, PhysicalEnclaved), PhysicalEnclaved},
213+
{std::make_pair<ConnectionType, ConnectionType>(Physical, Plugin), Invalid},
214+
// DelayedEnclaved + x
215+
{std::make_pair<ConnectionType, ConnectionType>(DelayedEnclaved, Normal), DelayedEnclaved},
216+
{std::make_pair<ConnectionType, ConnectionType>(DelayedEnclaved, Delayed), DelayedEnclaved},
217+
{std::make_pair<ConnectionType, ConnectionType>(DelayedEnclaved, Enclaved), DelayedEnclaved},
218+
{std::make_pair<ConnectionType, ConnectionType>(DelayedEnclaved, Physical), Invalid}, // !!!
219+
{std::make_pair<ConnectionType, ConnectionType>(DelayedEnclaved, DelayedEnclaved), DelayedEnclaved},
220+
{std::make_pair<ConnectionType, ConnectionType>(DelayedEnclaved, PhysicalEnclaved), Invalid}, // !!!
221+
{std::make_pair<ConnectionType, ConnectionType>(DelayedEnclaved, Plugin), Invalid},
222+
// PhysicalEnclaved + x
223+
{std::make_pair<ConnectionType, ConnectionType>(PhysicalEnclaved, Normal), PhysicalEnclaved},
224+
{std::make_pair<ConnectionType, ConnectionType>(PhysicalEnclaved, Delayed), Invalid}, // !!!
225+
{std::make_pair<ConnectionType, ConnectionType>(PhysicalEnclaved, Enclaved), PhysicalEnclaved},
226+
{std::make_pair<ConnectionType, ConnectionType>(PhysicalEnclaved, Physical), PhysicalEnclaved},
227+
{std::make_pair<ConnectionType, ConnectionType>(PhysicalEnclaved, DelayedEnclaved), Invalid}, // !!!
228+
{std::make_pair<ConnectionType, ConnectionType>(PhysicalEnclaved, PhysicalEnclaved), PhysicalEnclaved},
229+
{std::make_pair<ConnectionType, ConnectionType>(PhysicalEnclaved, Plugin), Invalid},
230+
// Plugin + x = Invalid
231+
{std::make_pair<ConnectionType, ConnectionType>(Plugin, Normal), Invalid}, // !!!
232+
{std::make_pair<ConnectionType, ConnectionType>(Plugin, Delayed), Invalid}, // !!!
233+
{std::make_pair<ConnectionType, ConnectionType>(Plugin, Enclaved), Invalid}, // !!!
234+
{std::make_pair<ConnectionType, ConnectionType>(Plugin, Physical), Invalid}, // !!!
235+
{std::make_pair<ConnectionType, ConnectionType>(Plugin, DelayedEnclaved), Invalid}, // !!!
236+
{std::make_pair<ConnectionType, ConnectionType>(Plugin, PhysicalEnclaved), Invalid}, // !!!
237+
{std::make_pair<ConnectionType, ConnectionType>(Plugin, Plugin), Invalid}, // !!!
238+
};
239+
240+
// getting all the sources from the graph
241+
auto keys = this->keys();
242+
243+
std::vector<E> has_downstreams{};
244+
std::copy_if(keys.begin(), keys.end(), std::back_inserter(has_downstreams),
245+
[](auto element) { return element->connected_to_downstream_actions(); });
246+
247+
std::vector<E> has_upstreams{};
248+
std::copy_if(keys.begin(), keys.end(), std::back_inserter(has_upstreams),
249+
[](auto element) { return element->connected_to_upstream_actions(); });
250+
251+
// generating all the possible destinations for all sources
252+
for (auto* source : has_upstreams) {
253+
auto spanning_tree = naive_spanning_tree(source);
254+
255+
for (auto& path : spanning_tree) {
256+
ConnectionProperties merged_properties{};
257+
auto* final_destination = std::get<2>(*std::begin(path));
258+
std::size_t current_rating = 0;
259+
260+
for (auto edge : path) {
261+
auto property = std::get<1>(edge);
262+
// auto source_port = std::get<0>(edge);
263+
auto destination_port = std::get<2>(edge);
264+
265+
current_rating += destination_port->rating();
266+
267+
if (current_rating > 0) {
268+
auto return_type =
269+
construction_table[std::pair<ConnectionType, ConnectionType>(merged_properties.type_, property.type_)];
270+
// invalid will split the connections
271+
if (return_type == Invalid) {
272+
// first add connection until this point
273+
optimized_graph.add_edge(destination_port, final_destination, merged_properties); // NOLINT
274+
275+
// resetting the properties and destination_port
276+
final_destination = destination_port;
277+
merged_properties = property;
278+
279+
} else {
280+
281+
// merging the connections
282+
merged_properties.type_ = return_type;
283+
284+
// adding up delays
285+
merged_properties.delay_ += property.delay_;
286+
287+
// updating target enclave if not nullptr
288+
merged_properties.enclave_ =
289+
(property.enclave_ != nullptr) ? property.enclave_ : merged_properties.enclave_;
290+
}
291+
}
292+
}
293+
optimized_graph.add_edge(std::get<0>(*(std::end(path) - 1)), final_destination, merged_properties);
294+
}
295+
}
296+
}
297+
191298
friend auto operator<<(std::ostream& outstream, const Graph& graph) -> std::ostream& {
192299
for (auto const& [source, destinations] : graph.graph_) {
193300
for (auto destination : destinations) {

include/reactor-cpp/port.hh

+12-2
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "assert.hh"
1616
#include "connection_properties.hh"
1717
#include "fwd.hh"
18+
#include "graph.hh"
1819
#include "multiport.hh"
1920
#include "reactor_element.hh"
2021
#include "value_ptr.hh"
@@ -23,7 +24,7 @@ namespace reactor {
2324

2425
enum class PortType { Input, Output, Delay };
2526

26-
class BasePort : public ReactorElement {
27+
class BasePort : public ReactorElement, public GraphElement {
2728
private:
2829
BasePort* inward_binding_{nullptr};
2930
std::set<BasePort*> outward_bindings_{};
@@ -43,6 +44,8 @@ protected:
4344
: ReactorElement(name, match_port_enum(type), container)
4445
, type_(type) {}
4546

47+
~BasePort() noexcept override = default;
48+
4649
void register_dependency(Reaction* reaction, bool is_trigger) noexcept;
4750
void register_antidependency(Reaction* reaction) noexcept;
4851
virtual void cleanup() = 0;
@@ -93,7 +96,6 @@ public:
9396
[[nodiscard]] inline auto has_dependencies() const noexcept -> bool { return !dependencies_.empty(); }
9497
[[nodiscard]] inline auto has_anti_dependencies() const noexcept -> bool { return !anti_dependencies_.empty(); }
9598
[[nodiscard]] inline auto has_triggers() const noexcept -> bool { return !triggers_.empty(); }
96-
[[nodiscard]] inline auto rating() const noexcept -> std::size_t { return triggers_.size() + dependencies_.size(); }
9799

98100
[[nodiscard]] inline auto inward_binding() const noexcept -> BasePort* { return inward_binding_; }
99101
[[nodiscard]] inline auto outward_bindings() const noexcept -> const auto& { return outward_bindings_; }
@@ -103,6 +105,14 @@ public:
103105
[[nodiscard]] inline auto anti_dependencies() const noexcept -> const auto& { return anti_dependencies_; }
104106
[[nodiscard]] inline auto port_type() const noexcept -> PortType { return type_; }
105107

108+
[[nodiscard]] auto connected_to_downstream_actions() const noexcept -> bool final {
109+
return has_dependencies() || has_triggers();
110+
};
111+
[[nodiscard]] auto connected_to_upstream_actions() const noexcept -> bool final { return has_anti_dependencies(); };
112+
[[nodiscard]] auto rating() const noexcept -> std::size_t final {
113+
return dependencies_.size() + triggers_.size() + ((set_callback_ != nullptr) ? 1 : 0);
114+
}
115+
106116
void register_set_callback(const PortCallback& callback);
107117
void register_clean_callback(const PortCallback& callback);
108118

0 commit comments

Comments
 (0)