Skip to content

Commit a1bd406

Browse files
committed
add basic strip method for the graph
1 parent 9ad576d commit a1bd406

File tree

5 files changed

+247
-130
lines changed

5 files changed

+247
-130
lines changed

include/reactor-cpp/environment.hh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ public:
9191
}
9292

9393
void optimize();
94+
void expand_and_merge();
95+
void strip_and_optimize();
9496

9597
void register_reactor(Reactor* reactor);
9698
void register_port(BasePort* port) noexcept;

include/reactor-cpp/graph.hh

Lines changed: 93 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ namespace reactor {
1919
template <class E, class P> class Graph {
2020
private:
2121
std::map<E, std::vector<std::pair<P, E>>> graph_;
22+
std::size_t index{0};
23+
std::map<E, std::string> name_map{};
2224

2325
// custom compare operator this is required if u want special key values in std::map
2426
// this is required for the Graph::get_edges() method
@@ -38,6 +40,8 @@ public:
3840
: graph_(std::move(graph.graph_)) {}
3941
~Graph() noexcept = default;
4042

43+
using Path = std::vector<std::pair<P, E>>;
44+
4145
auto operator=(const Graph other) noexcept -> Graph& {
4246
graph_ = other.graph_;
4347
return *this;
@@ -51,7 +55,7 @@ public:
5155
// adds a single edge to the graph structure
5256
void add_edge(E source, E destination, P properties) noexcept {
5357
if (graph_.find(source) == std::end(graph_)) {
54-
std::vector<std::pair<P, E>> edges{std::make_pair(properties, destination)};
58+
Path edges{std::make_pair(properties, destination)};
5559
graph_[source] = edges;
5660
} else {
5761
graph_[source].emplace_back(properties, destination);
@@ -85,33 +89,88 @@ public:
8589
return keys;
8690
}
8791

88-
// returns the spanning tree of a given source including properties
89-
[[nodiscard]] auto spanning_tree(E source) noexcept -> std::map<E, std::vector<std::pair<P, E>>> {
90-
std::map<E, std::vector<std::pair<P, E>>> tree{};
91-
std::vector<E> work_nodes{source};
92-
93-
while (!work_nodes.empty()) {
94-
auto parent = *work_nodes.begin();
95-
96-
for (auto child : graph_[parent]) {
97-
// figuring out the properties until this node
98-
std::vector<std::pair<P, E>> parent_properties{};
99-
if (tree.find(parent) != std::end(tree)) {
100-
// this if should always be the case except the first time when tree is empty
101-
parent_properties = tree[parent]; // TODO: make sure this is a copy otherwise we change this properties as
102-
// well
103-
}
92+
auto name_resolver(E object) -> std::string {
93+
char names[] = "ABCDEFGHIJKLMNOPQRSTUVGXYZabcdefghijklmnopqrstuvgxyz";
94+
if(name_map.find(object) == std::end(name_map)) {
95+
name_map[object] = names[index];
96+
index++;
97+
return std::string{names[index - 1], 1};
98+
}
99+
return name_map[object];
100+
}
101+
102+
// the return type looks a little bit cursed what is happening here ?
103+
// we have a map from the destination as a key to a list of paths through the graph.
104+
// A path here is modelled by a list of edges (with properties and the next vertex).
105+
auto naive_spanning_tree(E source) noexcept -> std::vector<std::vector<std::pair<P, E>>> {
106+
auto result = recursive_spanning_tree(source, std::vector<E>{});
107+
108+
std::string mermaid_string = "graph TD;\n";
109+
110+
for (auto path : result ) {
111+
mermaid_string += std::string(" ") + name_resolver(source);
112+
for (auto edge : path) {
113+
mermaid_string += std::string("-->")+ name_resolver(edge.second);
114+
}
115+
mermaid_string += std::string(";\n");
116+
117+
}
118+
//std::cout << "EDGE: \n" << mermaid_string << std::endl;
119+
return result;
120+
}
121+
122+
123+
// this function goes recursively though the graph and tries to find every possible path
124+
auto recursive_spanning_tree(E source_node, std::vector<E> visited_nodes) -> std::vector<std::vector<std::pair<P, E>>> {
125+
std::vector<Path> paths{};
126+
127+
if (graph_[source_node].empty()) {
128+
return std::vector<Path>{Path{}};
129+
}
130+
131+
for (auto child : graph_[source_node]) {
132+
E current_node = child.second;
133+
134+
// means this node has not been visited yey
135+
if (std::find(std::begin(visited_nodes), std::end(visited_nodes), current_node) == std::end(visited_nodes)) {
104136

105-
// appending the new property and inserting into the tree
106-
parent_properties.push_back(child);
107-
work_nodes.push_back(child.second);
108-
tree[child.second] = parent_properties;
137+
// creating a temporary vector where the currently selected vertex is appended
138+
auto temp_nodes = visited_nodes;
139+
temp_nodes.push_back(current_node);
140+
141+
for (auto path: recursive_spanning_tree(current_node, temp_nodes)) {
142+
path.insert(std::begin(path), child);
143+
paths.push_back(path);
144+
}
109145
}
146+
}
147+
148+
return paths;
149+
}
110150

111-
work_nodes.erase(std::begin(work_nodes));
151+
auto shortest_path(E source, E destination) -> std::optional<Path> {
152+
// TODO: maybe build proper djikstra here
153+
154+
auto spanning_tre = naive_spanning_tree(source);
155+
std::vector<Path> relevant_paths{};
156+
157+
std::copy_if(spanning_tre.begin(), spanning_tre.end(), std::back_inserter(relevant_paths), [destination](Path path) {
158+
return (*path.end()).second == destination;
159+
});
160+
161+
if (relevant_paths.empty()) {
162+
return std::nullopt;
112163
}
113164

114-
return tree;
165+
Path best_path = *relevant_paths.begin();
166+
167+
for (auto path : relevant_paths) {
168+
if (path.size() < best_path.size()) {
169+
best_path = path;
170+
}
171+
}
172+
173+
return best_path;
115174
}
116175

117176
[[nodiscard]] auto get_destinations(E source) const noexcept -> std::vector<std::pair<P, E>> {
@@ -126,6 +185,17 @@ public:
126185
}
127186
}
128187

188+
auto to_mermaid() noexcept -> std::string {
189+
std::string mermaid_string = "graph TD;\n";
190+
191+
for (const auto& [source, destinations] : graph_) {
192+
for (auto dest : destinations) {
193+
mermaid_string += std::string(" ") + name_resolver(source) + std::string("-->") + name_resolver(dest.second) + std::string(";\n");
194+
}
195+
}
196+
return mermaid_string;
197+
}
198+
129199
friend auto operator<<(std::ostream& outstream, const Graph& graph) -> std::ostream& {
130200
for (auto const& [source, destinations] : graph.graph_) {
131201
for (auto destination : destinations) {

include/reactor-cpp/port.hh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ public:
9292
[[nodiscard]] inline auto has_outward_bindings() const noexcept -> bool { return !outward_bindings_.empty(); }
9393
[[nodiscard]] inline auto has_dependencies() const noexcept -> bool { return !dependencies_.empty(); }
9494
[[nodiscard]] inline auto has_anti_dependencies() const noexcept -> bool { return !anti_dependencies_.empty(); }
95+
[[nodiscard]] inline auto has_triggers() const noexcept -> bool { return !triggers_.empty(); }
9596

9697
[[nodiscard]] inline auto inward_binding() const noexcept -> BasePort* { return inward_binding_; }
9798
[[nodiscard]] inline auto outward_bindings() const noexcept -> const auto& { return outward_bindings_; }

lib/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ else()
2222
set(REACTOR_CPP_INCLUDE "include")
2323
endif()
2424

25-
add_library(${LIB_TARGET} SHARED ${SOURCE_FILES})
25+
add_library(${LIB_TARGET} STATIC ${SOURCE_FILES})
2626
target_include_directories(${LIB_TARGET} PUBLIC
2727
"$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>"
2828
"$<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include>"

0 commit comments

Comments
 (0)