diff --git a/src/faebryk/core/cpp/include/graph/graph.hpp b/src/faebryk/core/cpp/include/graph/graph.hpp index a3dfe595..fcc2d1cb 100644 --- a/src/faebryk/core/cpp/include/graph/graph.hpp +++ b/src/faebryk/core/cpp/include/graph/graph.hpp @@ -144,7 +144,7 @@ class GraphInterface { template static std::shared_ptr factory(); std::unordered_set get_gif_edges(); - std::unordered_map &get_edges(); + Map &get_edges(); std::optional is_connected(GI_ref_weak to); Graph_ref get_graph(); std::unordered_set get_connected_nodes(std::vector types); @@ -200,23 +200,28 @@ class Path { public: Path(/*const*/ GI_ref_weak path_head); Path(std::vector path); + Path(std::vector path, GI_ref_weak head); Path(const Path &other); Path(Path &&other); ~Path(); - std::vector path; + const std::vector path; - /*const*/ Link_weak_ref get_link(Edge edge) /*const*/; - std::optional last_edge() /*const*/; - std::optional last_tri_edge() /*const*/; - /*const*/ GI_ref_weak last() /*const*/; - /*const*/ GI_ref_weak first() /*const*/; - /*const*/ GI_ref_weak operator[](int idx) /*const*/; - size_t size() /*const*/; - bool contains(/*const*/ GI_ref_weak gif) /*const*/; - void iterate_edges(std::function visitor) /*const*/; - /*const*/ std::vector &get_path() /*const*/; - size_t index(/*const*/ GI_ref_weak gif) /*const*/; + /*const*/ Link_weak_ref get_link(Edge edge) const; + std::optional last_edge() const; + std::optional last_tri_edge() const; + /*const*/ GI_ref_weak last() const; + /*const*/ GI_ref_weak first() const; + /*const*/ GI_ref_weak operator[](int idx) const; + size_t size() const; + bool contains(/*const*/ GI_ref_weak gif) const; + void iterate_edges(std::function visitor) const; + const std::vector &get_path() const; + std::vector get_path_mut() const; + size_t index(/*const*/ GI_ref_weak gif) const; + + bool operator==(const Path &other) const; + bool starts_with(const Path &other) const; std::string str() const; }; @@ -237,7 +242,7 @@ class Graph { static Graph_ref merge_graphs(Graph_ref g1, Graph_ref g2); std::unordered_set get_gif_edges(GI_ref_weak from); - std::unordered_map &get_edges(GI_ref_weak from); + Map &get_edges(GI_ref_weak from); Graph(); ~Graph(); diff --git a/src/faebryk/core/cpp/include/pathfinder/bfs.hpp b/src/faebryk/core/cpp/include/pathfinder/bfs.hpp index 57270aba..c879541a 100644 --- a/src/faebryk/core/cpp/include/pathfinder/bfs.hpp +++ b/src/faebryk/core/cpp/include/pathfinder/bfs.hpp @@ -17,6 +17,7 @@ struct PathStackElement { Node::Type parent_type; Node::Type child_type; /*const*/ GI_parent_ref_weak parent_gif; + GI_parent_ref_weak child_gif; std::string name; bool up; @@ -37,21 +38,68 @@ using UnresolvedStack = std::vector; struct PathData { UnresolvedStack unresolved_stack; PathStack split_stack; + bool not_complete = false; }; -class BFSPath : public Path { +namespace std { +template <> struct hash { + size_t operator()(const Path &p) const noexcept { + const GI_refs_weak &path = p.get_path(); + size_t hash = 0; + for (auto &gif : path) { + hash = hash * 31 + std::hash{}(gif); + } + return hash; + } +}; +} // namespace std + +class BFSPath : public Path, public std::enable_shared_from_this { std::shared_ptr path_data; public: + /** + * @brief Confidence that this path might become 'valid' path + * + * 0 < confidence <= 1 + * + * confidence < 1 := weak path + */ double confidence = 1.0; + /** + * @brief Removes the path from the search + * + */ bool filtered = false; + /** + * @brief Hibernates the path + * + * Hibernated BFS paths are not visited as long as they are hibernated. + */ + bool hibernated = false; + /** + * @brief Stops the BFS search + * + */ bool stop = false; + /** + * @brief Notifies BFS that it woke up other paths + * + */ + bool wake_signal = false; + + /** + * @brief Notifies BFS that the path has become strong after being weak + * + */ + bool strong_signal = false; + BFSPath(/*const*/ GI_ref_weak path_head); BFSPath(const BFSPath &other); BFSPath(const BFSPath &other, /*const*/ GI_ref_weak new_head); - BFSPath(BFSPath &&other); - BFSPath operator+(/*const*/ GI_ref_weak gif); + BFSPath(BFSPath &&other) = delete; + std::shared_ptr operator+(/*const*/ GI_ref_weak gif); PathData &get_path_data_mut(); PathData &get_path_data() /*const*/; diff --git a/src/faebryk/core/cpp/include/pathfinder/pathfinder.hpp b/src/faebryk/core/cpp/include/pathfinder/pathfinder.hpp index df8e3ec6..0ea3d4ae 100644 --- a/src/faebryk/core/cpp/include/pathfinder/pathfinder.hpp +++ b/src/faebryk/core/cpp/include/pathfinder/pathfinder.hpp @@ -39,21 +39,64 @@ struct Filter { bool exec(PathFinder *pf, BFSPath &p); }; +struct SplitState { + + /** + * @brief Path that led to this split + * + */ + Path split_prefix; + + /** + * @brief Branch complete + * + * All children have complete suffix path from here on + */ + bool complete = false; + + bool waiting = false; + + /** + * @brief All paths that are from this split on complete further (& fully valid) + * + */ + Map>> suffix_complete_paths; + + /** + * @brief All paths that are hibernated per child + * + */ + Map>> wait_paths; + + SplitState(const BFSPath &path); + + /** + * @brief Parent gif of this split + * + * (always split_prefix.last()) + */ + GI_ref_weak split_point() const; +}; + class PathFinder { - std::vector multi_paths; + // TODO I think we should use PathStack (slightly modified) instead of Path + // since non-hierarchical links have no influence on the split + Map> split; size_t path_cnt = 0; bool _count(BFSPath &p); bool _filter_path_by_node_type(BFSPath &p); bool _filter_path_gif_type(BFSPath &p); bool _filter_path_by_dead_end_split(BFSPath &p); - bool _build_path_stack(BFSPath &p); + bool _build_path_stack_and_handle_splits(BFSPath &p); bool _filter_path_by_end_in_self_gif(BFSPath &p); bool _filter_path_same_end_type(BFSPath &p); bool _filter_path_by_stack(BFSPath &p); bool _filter_shallow(BFSPath &p); bool _filter_conditional_link(BFSPath &p); + bool _handle_valid_split_branch(BFSPath &p); std::vector _filter_paths_by_split_join(std::vector &paths); + bool _filter_incomplete(BFSPath &p); public: PathFinder(); diff --git a/src/faebryk/core/cpp/include/util.hpp b/src/faebryk/core/cpp/include/util.hpp index 14e57c35..687fd4d8 100644 --- a/src/faebryk/core/cpp/include/util.hpp +++ b/src/faebryk/core/cpp/include/util.hpp @@ -5,9 +5,12 @@ #pragma once #include +#include #include #include #include +#include +#include #if GLOBAL_PRINTF_DEBUG #else @@ -53,4 +56,13 @@ inline std::string formatted_ptr(void *ptr) { // return ss.str(); //} +template +inline std::unordered_map> groupby(const std::vector &vec, + std::function f) { + std::unordered_map> out; + for (auto &t : vec) { + out[f(t)].push_back(t); + } + return out; +} } // namespace util diff --git a/src/faebryk/core/cpp/src/graph/graph.cpp b/src/faebryk/core/cpp/src/graph/graph.cpp index 0badf686..fe8351e0 100644 --- a/src/faebryk/core/cpp/src/graph/graph.cpp +++ b/src/faebryk/core/cpp/src/graph/graph.cpp @@ -98,7 +98,7 @@ std::unordered_set Graph::get_gif_edges(GI_ref_weak from) { return this->e_cache_simple[from]; } -std::unordered_map &Graph::get_edges(GI_ref_weak from) { +Map &Graph::get_edges(GI_ref_weak from) { return this->e_cache[from]; } diff --git a/src/faebryk/core/cpp/src/graph/graphinterface.cpp b/src/faebryk/core/cpp/src/graph/graphinterface.cpp index d64fc6cd..856a5056 100644 --- a/src/faebryk/core/cpp/src/graph/graphinterface.cpp +++ b/src/faebryk/core/cpp/src/graph/graphinterface.cpp @@ -111,7 +111,7 @@ Set GraphInterface::get_gif_edges() { return this->G->get_gif_edges(this); } -std::unordered_map &GraphInterface::get_edges() { +Map &GraphInterface::get_edges() { return this->G->get_edges(this); } diff --git a/src/faebryk/core/cpp/src/graph/links.cpp b/src/faebryk/core/cpp/src/graph/links.cpp index 0e21e8bc..5a9ba47c 100644 --- a/src/faebryk/core/cpp/src/graph/links.cpp +++ b/src/faebryk/core/cpp/src/graph/links.cpp @@ -208,6 +208,13 @@ LinkDirectConditional::LinkDirectConditional(FilterF filter, : LinkDirect() , filter(filter) , needs_only_first_in_path(needs_only_first_in_path) { + + // TODO 1 actually check implied is on + // TODO 2 implement this + // not easy, needs some changes in how split paths are handled + if (!needs_only_first_in_path) { + throw std::runtime_error("No support for path conditions with implied links on"); + } } LinkDirectConditional::LinkDirectConditional(FilterF filter, @@ -216,6 +223,14 @@ LinkDirectConditional::LinkDirectConditional(FilterF filter, : LinkDirect(from, to) , filter(filter) , needs_only_first_in_path(needs_only_first_in_path) { + + // TODO 1 actually check implied is on + // TODO 2 implement this + // not easy, needs some changes in how split paths are handled + if (!needs_only_first_in_path) { + throw std::runtime_error("No support for path conditions with implied links on"); + } + this->set_connections(from, to); } diff --git a/src/faebryk/core/cpp/src/graph/path.cpp b/src/faebryk/core/cpp/src/graph/path.cpp index 591526f0..7e237156 100644 --- a/src/faebryk/core/cpp/src/graph/path.cpp +++ b/src/faebryk/core/cpp/src/graph/path.cpp @@ -22,38 +22,46 @@ Path::Path(Path &&other) : path(std::move(other.path)) { } +Path::Path(std::vector path, GI_ref_weak head) + : path([&path, &head]() { + auto new_path = path; + new_path.push_back(head); + return new_path; + }()) { +} + Path::~Path() { } -/*const*/ Link_weak_ref Path::get_link(Edge edge) /*const*/ { +/*const*/ Link_weak_ref Path::get_link(Edge edge) const { auto out = edge.from->is_connected(edge.to); assert(out); return out->get(); } -std::optional Path::last_edge() /*const*/ { +std::optional Path::last_edge() const { if (path.size() < 2) { return {}; } return Edge{path[path.size() - 2], path.back()}; } -std::optional Path::last_tri_edge() /*const*/ { +std::optional Path::last_tri_edge() const { if (path.size() < 3) { return {}; } return std::make_tuple(path[path.size() - 3], path[path.size() - 2], path.back()); } -/*const*/ GI_ref_weak Path::last() /*const*/ { +/*const*/ GI_ref_weak Path::last() const { return path.back(); } -/*const*/ GI_ref_weak Path::first() /*const*/ { +/*const*/ GI_ref_weak Path::first() const { return path.front(); } -/*const*/ GI_ref_weak Path::operator[](int idx) /*const*/ { +/*const*/ GI_ref_weak Path::operator[](int idx) const { if (idx < 0) { idx = path.size() + idx; } @@ -63,15 +71,15 @@ std::optional Path::last_tri_edge() /*const*/ { return path[idx]; } -size_t Path::size() /*const*/ { +size_t Path::size() const { return path.size(); } -bool Path::contains(/*const*/ GI_ref_weak gif) /*const*/ { +bool Path::contains(/*const*/ GI_ref_weak gif) const { return std::find(path.begin(), path.end(), gif) != path.end(); } -void Path::iterate_edges(std::function visitor) /*const*/ { +void Path::iterate_edges(std::function visitor) const { for (size_t i = 1; i < path.size(); i++) { Edge edge{path[i - 1], path[i]}; bool res = visitor(edge); @@ -81,11 +89,15 @@ void Path::iterate_edges(std::function visitor) /*const*/ { } } -/*const*/ std::vector &Path::get_path() /*const*/ { - return path; +const std::vector &Path::get_path() const { + return this->path; } -size_t Path::index(/*const*/ GI_ref_weak gif) /*const*/ { +std::vector Path::get_path_mut() const { + return std::vector(this->path); +} + +size_t Path::index(/*const*/ GI_ref_weak gif) const { return std::distance(path.begin(), std::find(path.begin(), path.end(), gif)); } @@ -99,3 +111,14 @@ std::string Path::str() const { ss << "]"; return ss.str(); } + +bool Path::operator==(const Path &other) const { + return this->path == other.path; +} + +bool Path::starts_with(const Path &other) const { + if (other.path.size() > this->path.size()) { + return false; + } + return std::equal(other.path.begin(), other.path.end(), this->path.begin()); +} diff --git a/src/faebryk/core/cpp/src/pathfinder/bfs.cpp b/src/faebryk/core/cpp/src/pathfinder/bfs.cpp index fcb0a71c..b6383d1e 100644 --- a/src/faebryk/core/cpp/src/pathfinder/bfs.cpp +++ b/src/faebryk/core/cpp/src/pathfinder/bfs.cpp @@ -53,25 +53,17 @@ BFSPath::BFSPath(const BFSPath &other) } BFSPath::BFSPath(const BFSPath &other, /*const*/ GI_ref_weak new_head) - : Path(other) + : Path(other.get_path(), new_head) , path_data(other.path_data) , confidence(other.confidence) , filtered(other.filtered) , stop(other.stop) { - path.push_back(new_head); assert(!other.filtered); } -BFSPath::BFSPath(BFSPath &&other) - : Path(std::move(other)) - , path_data(std::move(other.path_data)) - , confidence(other.confidence) - , filtered(other.filtered) - , stop(other.stop) { -} +std::shared_ptr BFSPath::operator+(/*const*/ GI_ref_weak gif) { -BFSPath BFSPath::operator+(/*const*/ GI_ref_weak gif) { - return BFSPath(*this, gif); + return std::make_shared(*this, gif); } PathData &BFSPath::get_path_data_mut() { @@ -109,47 +101,70 @@ void bfs_visit(/*const*/ GI_ref_weak root, std::function visito auto node_count = root->get_graph()->node_count(); std::vector visited(node_count, false); std::vector visited_weak(node_count, false); - std::deque open_path_queue; + std::deque> open_path_queue; + std::deque> hibernated_paths; - auto handle_path = [&](BFSPath path) { + auto handle_path = [&](std::shared_ptr path) { pc.pause(); pc_filter.resume(); - visitor(path); + visitor(*path); pc_filter.pause(); pc.resume(); - if (path.stop) { + if (path->stop) { open_path_queue.clear(); return; } - if (path.filtered) { + if (path->wake_signal) { + for (auto it = hibernated_paths.begin(); it != hibernated_paths.end();) { + if (!it->get()->filtered) { + hibernated_paths.erase(it); + } else if (!it->get()->hibernated) { + open_path_queue.push_back(*it); + it = hibernated_paths.erase(it); + // + } else { + ++it; + } + } + } + + if (path->filtered) { return; } pc_set_insert.resume(); - visited_weak[path.last()->v_i] = true; + visited_weak[path->last()->v_i] = true; - if (path.strong()) { - visited[path.last()->v_i] = true; + if (path->strong_signal) { + for (auto &v : path->get_path()) { + visited[v->v_i] = true; + } + } else if (path->strong()) { + visited[path->last()->v_i] = true; } pc_set_insert.pause(); pc_deque_insert.resume(); - open_path_queue.push_back(std::move(path)); + if (path->hibernated) { + hibernated_paths.push_back(path); + } else { + open_path_queue.push_back(path); + } pc_deque_insert.pause(); }; pc_setup.pause(); - handle_path(std::move(BFSPath(root))); + handle_path(std::make_shared(root)); pc_search.resume(); while (!open_path_queue.empty()) { - auto path = std::move(open_path_queue.front()); + auto path = open_path_queue.front(); open_path_queue.pop_front(); pc_edges.resume(); - auto edges = path.last()->get_gif_edges(); + auto edges = path->last()->get_gif_edges(); pc_edges.pause(); for (auto &neighbour : edges) { pc_check_visited.resume(); @@ -157,17 +172,17 @@ void bfs_visit(/*const*/ GI_ref_weak root, std::function visito pc_check_visited.pause(); continue; } - if (visited_weak[neighbour->v_i] && path.contains(neighbour)) { + if (visited_weak[neighbour->v_i] && path->contains(neighbour)) { pc_check_visited.pause(); continue; } pc_check_visited.pause(); pc_new_path.resume(); - auto new_path = path + neighbour; + auto new_path = *path + neighbour; pc_new_path.pause(); pc_search.pause(); - handle_path(std::move(new_path)); + handle_path(new_path); pc_search.resume(); } } diff --git a/src/faebryk/core/cpp/src/pathfinder/bfs.hpp b/src/faebryk/core/cpp/src/pathfinder/bfs.hpp new file mode 100644 index 00000000..0519ecba --- /dev/null +++ b/src/faebryk/core/cpp/src/pathfinder/bfs.hpp @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/faebryk/core/cpp/src/pathfinder/pathfinder.cpp b/src/faebryk/core/cpp/src/pathfinder/pathfinder.cpp index da79e4f6..9923f375 100644 --- a/src/faebryk/core/cpp/src/pathfinder/pathfinder.cpp +++ b/src/faebryk/core/cpp/src/pathfinder/pathfinder.cpp @@ -6,9 +6,74 @@ #include "graph/links.hpp" #include "pathfinder/bfs.hpp" #include "pathfinder/pathcounter.hpp" +#include #include #include +// Util -------------------------------------------------------------------------------- +GI_refs_weak get_split_children(GI_ref_weak split_point) { + // TODO this can be optimized I think + auto children = split_point->get_node()->get_children( + true, {{Node::Type::get_moduleinterface_type()}}, false); + GI_refs_weak out; + for (auto &c : children) { + out.push_back(c->get_parent_gif().get()); + } + return out; +} + +SplitState::SplitState(const BFSPath &path) + : split_prefix(std::vector(path.get_path().begin(), path.get_path().end() - 1)) { + for (auto &gif : get_split_children(split_point())) { + suffix_complete_paths[gif]; + wait_paths[gif]; + } +} + +GI_ref_weak SplitState::split_point() const { + return split_prefix.last(); +} + +std::optional _extend_path_hierarchy_stack(Edge &edge) { + bool up = GraphInterfaceHierarchical::is_uplink(edge.from, edge.to); + if (!up && !GraphInterfaceHierarchical::is_downlink(edge.from, edge.to)) { + return {}; + } + auto child_gif = dynamic_cast(up ? edge.from : edge.to); + auto parent_gif = dynamic_cast(up ? edge.to : edge.from); + + auto name = child_gif->get_parent()->second; + return PathStackElement{parent_gif->get_node()->get_type(), + child_gif->get_node()->get_type(), + parent_gif, + child_gif, + name, + up}; +} + +void _extend_fold_stack(PathStackElement &elem, UnresolvedStack &unresolved_stack, + PathStack &split_stack) { + if (!unresolved_stack.empty() && unresolved_stack.back().match(elem)) { + // TODO why was this here? + // auto split = unresolved_stack.back().split; + // if (split) { + // split_stack.push_back(elem); + // } + unresolved_stack.pop_back(); + } else { + bool multi_child = get_split_children(elem.parent_gif).size() > 1; + // if down and multipath -> split + bool split = !elem.up && multi_child; + + unresolved_stack.push_back(UnresolvedStackElement{elem, split}); + if (split) { + split_stack.push_back(elem); + } + } +} + +// ------------------------------------------------------------------------------------- + // PathFinder implementations PathFinder::PathFinder() : filters{ @@ -53,7 +118,7 @@ PathFinder::PathFinder() }, }, Filter{ - .filter = &PathFinder::_build_path_stack, + .filter = &PathFinder::_build_path_stack_and_handle_splits, .discovery = true, .counter = Counter{ @@ -84,6 +149,14 @@ PathFinder::PathFinder() .name = "stack", }, }, + Filter{ + .filter = &PathFinder::_handle_valid_split_branch, + .discovery = false, + .counter = + Counter{ + .name = "valid split branch", + }, + }, } { } @@ -106,6 +179,12 @@ bool PathFinder::run_filters(BFSPath &p) { return true; } +/* +filtered: contains illegal links +valid: reached dst with no illegal links +complete: all splits have a valid branch +*/ + std::pair, std::vector> PathFinder::find_paths(Node_ref src, std::vector dst) { if (!src->get_type().is_moduleinterface()) { @@ -119,17 +198,26 @@ PathFinder::find_paths(Node_ref src, std::vector dst) { dsts.insert(d); } - std::vector paths; + std::vector valid_paths; Counter total_counter{.name = "total", .total_counter = true}; PerfCounter pc_bfs; + // Valid paths BFS bfs_visit(src->get_self_gif().get(), [&](BFSPath &p) { bool res = total_counter.exec(this, &PathFinder::run_filters, p); if (!res) { return; } + + valid_paths.push_back(p); + + if (p.get_path_data().not_complete) { + return; + } + + // TODO consider removing this if the new path split pruning works well // shortcut if path to dst found auto last = p.last()->get_node(); if (dsts.contains(last)) { @@ -138,26 +226,21 @@ PathFinder::find_paths(Node_ref src, std::vector dst) { p.stop = true; } } - paths.push_back(p); }); printf("TIME: %3.2lf ms BFS\n", pc_bfs.ms()); - Counter counter_split_join{ - .name = "split join", - .multi = true, - }; - auto multi_paths = counter_split_join.exec_multi( - this, &PathFinder::_filter_paths_by_split_join, this->multi_paths); - - std::vector paths_out; - for (auto &p : paths) { - paths_out.push_back(Path(std::move(p.get_path()))); - } - for (auto &p : multi_paths) { - paths_out.push_back(Path(std::move(p.get_path()))); + // Complete paths + Counter incomplete_counter{.name = "incomplete", .total_counter = false}; + std::vector complete_paths; + for (auto &p : valid_paths) { + if (!incomplete_counter.exec(this, &PathFinder::_filter_incomplete, p)) { + continue; + } + complete_paths.push_back(Path(std::move(p.get_path()))); } + // Counters std::vector counters; for (auto &f : filters) { auto &counter = f.counter; @@ -166,12 +249,15 @@ PathFinder::find_paths(Node_ref src, std::vector dst) { } counters.push_back(counter); } - counters.push_back(counter_split_join); counters.push_back(total_counter); + counters.push_back(incomplete_counter); - return std::make_pair(paths_out, counters); + // Return + return std::make_pair(complete_paths, counters); } +// Filters ----------------------------------------------------------------------------- + bool PathFinder::_count(BFSPath &p) { path_cnt++; if (path_cnt % 50000 == 0) { @@ -202,40 +288,7 @@ bool PathFinder::_filter_path_same_end_type(BFSPath &p) { return p.last()->get_node()->get_type() == p.first()->get_node()->get_type(); } -std::optional _extend_path_hierarchy_stack(Edge &edge) { - bool up = GraphInterfaceHierarchical::is_uplink(edge.from, edge.to); - if (!up && !GraphInterfaceHierarchical::is_downlink(edge.from, edge.to)) { - return {}; - } - auto child_gif = dynamic_cast(up ? edge.from : edge.to); - auto parent_gif = dynamic_cast(up ? edge.to : edge.from); - - auto name = child_gif->get_parent()->second; - return PathStackElement{parent_gif->get_node()->get_type(), - child_gif->get_node()->get_type(), parent_gif, name, up}; -} - -void _extend_fold_stack(PathStackElement &elem, UnresolvedStack &unresolved_stack, - PathStack &split_stack) { - if (!unresolved_stack.empty() && unresolved_stack.back().match(elem)) { - auto split = unresolved_stack.back().split; - if (split) { - split_stack.push_back(elem); - } - unresolved_stack.pop_back(); - } else { - bool multi_child = elem.parent_gif->get_children().size() > 1; - // if down and multipath -> split - bool split = !elem.up && multi_child; - - unresolved_stack.push_back(UnresolvedStackElement{elem, split}); - if (split) { - split_stack.push_back(elem); - } - } -} - -bool PathFinder::_build_path_stack(BFSPath &p) { +bool PathFinder::_build_path_stack_and_handle_splits(BFSPath &p) { auto edge = p.last_edge(); if (!edge) { return true; @@ -246,12 +299,16 @@ bool PathFinder::_build_path_stack(BFSPath &p) { return true; } - auto &splits = p.get_path_data_mut(); - auto &unresolved_stack = splits.unresolved_stack; - auto &split_stack = splits.split_stack; + auto &data = p.get_path_data_mut(); + auto &unresolved_stack = data.unresolved_stack; + auto &split_stack = data.split_stack; size_t split_cnt = split_stack.size(); - if (split_cnt > 0 && path_cnt > PATH_LIMITS.no_weak) { + + // heuristic, stop weak paths after limit + // no need to check for extension first, since growth of split results earlier in + // stop + if (split_stack.size() > 0 && path_cnt > PATH_LIMITS.no_weak) { return false; } @@ -265,21 +322,155 @@ bool PathFinder::_build_path_stack(BFSPath &p) { return false; } + if (split_growth == 0) { + return true; + } + + assert(!elem->up); + + // handle split + data.not_complete = true; + + auto split_point = elem->parent_gif; + Path split_prefix(std::vector(p.get_path().begin(), p.get_path().end() - 1)); + auto &splits = this->split[split_point]; + + // check if split point already in split map + // if yes, hibernate, wait till other branches complete + if (splits.contains(split_prefix)) { + auto &split_state = splits.at(split_prefix); + // TODO this should never happen imo with Path as key + // later we will need this when using PathStack as key + if (split_state.complete) { + assert(false); + return false; + } + if (!split_state.waiting) { + p.hibernated = true; + split_state.wait_paths[elem->child_gif].push_back(p.shared_from_this()); + } + return true; + } + + // make new split + splits.emplace(split_prefix, SplitState{p}); + return true; } bool PathFinder::_filter_path_by_stack(BFSPath &p) { const auto splits = p.get_path_data(); auto &unresolved_stack = splits.unresolved_stack; + + return unresolved_stack.empty(); +} + +bool PathFinder::_handle_valid_split_branch(BFSPath &p) { + const auto splits = p.get_path_data(); auto &split_stack = splits.split_stack; - if (!unresolved_stack.empty()) { - return false; + if (split_stack.empty()) { + // Not a multi path branch + return true; } - if (!split_stack.empty()) { - this->multi_paths.push_back(p); - return false; + // strategy: + // - get all splits + // - check if all splits have a complete branch + // - if yes + // - remove all hibernated paths related to these splits + // because we only have to track this (or first) branch from here + // done implicitly by marking them strong from here, this results in + // all others not having neighbours to explore + // will also take the shortest/best path first + // - make all split valid paths complete + // - necessary because of implied paths tracking all conditional links + // - if no + // - hibernate (no need to keep on searching if not complete yet) + // - awake branches from split + + for (auto &split_elem : std::ranges::reverse_view(split_stack)) { + auto &split_point = split_elem.parent_gif; + auto &splits_at_point = this->split[split_point]; + for (auto &[split_prefix, split_state] : splits_at_point) { + if (!p.starts_with(split_prefix)) { + continue; + } + // found split_state for this path & split point + + split_state.suffix_complete_paths[split_elem.child_gif].push_back( + p.shared_from_this()); + if (split_state.complete) { + continue; + } + + // check complete branch + for (auto &[child_gif, paths] : split_state.suffix_complete_paths) { + // check if child complete (has path ending in same gif) + if (paths.size()) { + bool found = false; + for (auto &path : paths) { + if (path->last() == p.last()) { + found = true; + break; + } + } + if (found) { + continue; + } + } + // handle incomplete child branch + + // wake up branch for that child + auto &wait_paths = split_state.wait_paths[child_gif]; + if (wait_paths.size() == 0) { + // if not waiting paths we wait till they appear + // they will check themselves + split_state.waiting = true; + } else { + auto &back = wait_paths.back(); + // TODO maybe use queue instead of vector + back->hibernated = false; + wait_paths.pop_back(); + p.wake_signal = true; + } + + // wait till branch complete, or woken up to advance to the next mif + // in-case this was not the merging point yet + p.hibernated = true; + // no need to go lower in stack if not complete branch + return true; + } + + split_state.complete = true; + // remove all waiting paths + for (auto &[child_gif, wait_paths] : split_state.wait_paths) { + for (auto &wait_path : wait_paths) { + wait_path->filtered = true; + } + wait_paths.clear(); + } + // only single match here + break; + } + } + + auto root_split = this->split[split_stack.front().parent_gif]; + for (auto &[split_prefix, split_state] : root_split) { + if (!p.starts_with(split_prefix)) { + continue; + } + + for (auto &[child_gif, paths] : split_state.suffix_complete_paths) { + for (auto &path : paths) { + auto &data = path->get_path_data_mut(); + data.not_complete = false; + path->hibernated = false; + data.split_stack.clear(); + path->confidence = 1.0; + p.wake_signal = true; + } + } } return true; @@ -336,91 +527,90 @@ bool PathFinder::_filter_conditional_link(BFSPath &p) { return ok; } -template -std::unordered_map> groupby(const std::vector &vec, - std::function f) { - std::unordered_map> out; - for (auto &t : vec) { - out[f(t)].push_back(t); - } - return out; +bool PathFinder::_filter_incomplete(BFSPath &p) { + return !p.get_path_data().not_complete; } +// Multi path filters +// ------------------------------------------------------------------ + std::vector PathFinder::_filter_paths_by_split_join(std::vector &paths) { - std::unordered_set filtered; - std::unordered_map> split; - - // build split map - for (auto &p : paths) { - auto &splits = p.get_path_data(); - auto &unresolved_stack = splits.unresolved_stack; - auto &split_stack = splits.split_stack; - - assert(unresolved_stack.empty()); - assert(!split_stack.empty()); - - // printf("Path: %s\n", p.str().c_str()); - - for (auto &elem : split_stack) { - if (elem.up) { - // join - continue; - } - // split - split[elem.parent_gif].push_back(&p); - } - } - - // printf("Split map: %zu\n", split.size()); + // std::unordered_set filtered; + // Map> + // split; + + //// build split map + // for (auto &p : paths) { + // auto &splits = p.get_path_data(); + // auto &unresolved_stack = splits.unresolved_stack; + // auto &split_stack = splits.split_stack; + + // assert(unresolved_stack.empty()); + // assert(!split_stack.empty()); + + // // printf("Path: %s\n", p.str().c_str()); + + // for (auto &elem : split_stack) { + // if (elem.up) { + // // join + // continue; + // } + // // split + // split[elem.parent_gif].push_back(&p); + // } + //} + + //// printf("Split map: %zu\n", split.size()); + //// for (auto &[start_gif, split_paths] : split) { + //// printf(" Start gif[%zu]: %s\n", split_paths.size(), + //// start_gif->get_full_name().c_str()); + //// } + + //// check split map // for (auto &[start_gif, split_paths] : split) { - // printf(" Start gif[%zu]: %s\n", split_paths.size(), - // start_gif->get_full_name().c_str()); + // auto children = start_gif->get_node()->get_children( + // true, {{node::type::get_moduleinterface_type()}}, false); + // auto children_set = + // std::unordered_set(children.begin(), children.end()); + + // assert(split_paths.size()); + // auto index = split_paths[0]->index(start_gif); + + // std::function f = + // [index](/*const*/ BFSPath *p) -> /*const*/ GI_ref_weak { + // return p->last(); + // }; + // auto grouped_by_end = util::groupby(split_paths, f); + + // // printf("Grouped by end: %zu\n", grouped_by_end.size()); + // for (auto &[end_gif, grouped_paths] : grouped_by_end) { + // // printf(" End gif[%zu]: %s\n", grouped_paths.size(), + // // end_gif->get_full_name().c_str()); + + // std::unordered_set covered_children; + // for (auto &p : grouped_paths) { + // covered_children.insert((*p)[index + 1]->get_node()); + // } + // // printf(" Covered children: %zu/%zu\n", covered_children.size(), + // // children_set.size()); + + // if (covered_children != children_set) { + // filtered.insert(grouped_paths.begin(), grouped_paths.end()); + // continue; + // } + // } + //} + + // std::vector paths_out; + // for (BFSPath &p : paths) { + // if (filtered.contains(&p)) { + // continue; + // } + // p.confidence = 1.0; + // paths_out.push_back(p); // } - - // check split map - for (auto &[start_gif, split_paths] : split) { - auto children = start_gif->get_node()->get_children( - true, {{Node::Type::get_moduleinterface_type()}}, false); - auto children_set = - std::unordered_set(children.begin(), children.end()); - - assert(split_paths.size()); - auto index = split_paths[0]->index(start_gif); - - std::function f = - [index](/*const*/ BFSPath *p) -> /*const*/ GI_ref_weak { - return p->last(); - }; - auto grouped_by_end = groupby(split_paths, f); - - // printf("Grouped by end: %zu\n", grouped_by_end.size()); - for (auto &[end_gif, grouped_paths] : grouped_by_end) { - // printf(" End gif[%zu]: %s\n", grouped_paths.size(), - // end_gif->get_full_name().c_str()); - - std::unordered_set covered_children; - for (auto &p : grouped_paths) { - covered_children.insert((*p)[index + 1]->get_node()); - } - // printf(" Covered children: %zu/%zu\n", covered_children.size(), - // children_set.size()); - - if (covered_children != children_set) { - filtered.insert(grouped_paths.begin(), grouped_paths.end()); - continue; - } - } - } - - std::vector paths_out; - for (BFSPath &p : paths) { - if (filtered.contains(&p)) { - continue; - } - p.confidence = 1.0; - paths_out.push_back(p); - } - printf("Filtered paths: %zu\n", paths_out.size()); - return paths_out; + // printf("Filtered paths: %zu\n", paths_out.size()); + // return paths_out; + return {}; } diff --git a/src/faebryk/core/pathfinder.py b/src/faebryk/core/pathfinder.py index ec012ac7..5d444305 100644 --- a/src/faebryk/core/pathfinder.py +++ b/src/faebryk/core/pathfinder.py @@ -36,8 +36,8 @@ def find_paths(src: Node, dst: Sequence[Node]) -> Sequence[Path]: paths, counters = find_paths_cpp(src, dst) - if logger.isEnabledFor(logging.DEBUG): - logger.debug(Counters(counters)) + if logger.isEnabledFor(logging.INFO): + logger.info(Counters(counters)) return paths diff --git a/src/faebryk/library/SignalElectrical.py b/src/faebryk/library/SignalElectrical.py index 07d242c7..b585877d 100644 --- a/src/faebryk/library/SignalElectrical.py +++ b/src/faebryk/library/SignalElectrical.py @@ -21,7 +21,7 @@ def __init__(self) -> None: lambda path: LinkDirectConditionalFilterResult.FILTER_PASS if all(self.test(dst.node) for dst in path) else LinkDirectConditionalFilterResult.FILTER_FAIL_UNRECOVERABLE, - needs_only_first_in_path=False, + needs_only_first_in_path=True, ) # ---------------------------------------- diff --git a/test/core/test_core.py b/test/core/test_core.py index f8cccef6..a34d8f82 100644 --- a/test/core/test_core.py +++ b/test/core/test_core.py @@ -133,7 +133,8 @@ def test_inherited_link(self): class _Link(LinkDirectConditional): def __init__(self): super().__init__( - lambda path: LinkDirectConditionalFilterResult.FILTER_PASS + lambda path: LinkDirectConditionalFilterResult.FILTER_PASS, + needs_only_first_in_path=True, ) gif1 = GraphInterface() diff --git a/test/core/test_hierarchy_connect.py b/test/core/test_hierarchy_connect.py index 1c16a11a..617206ff 100644 --- a/test/core/test_hierarchy_connect.py +++ b/test/core/test_hierarchy_connect.py @@ -313,7 +313,10 @@ def test_specialize_link(): # test special link class _Link(LinkDirectConditional): def __init__(self): - super().__init__(lambda path: LinkDirectConditionalFilterResult.FILTER_PASS) + super().__init__( + lambda path: LinkDirectConditionalFilterResult.FILTER_PASS, + needs_only_first_in_path=True, + ) mifs = times(3, ModuleInterface) mifs_special = times(3, Specialized) @@ -561,4 +564,5 @@ def test_regression_rp2040_usb_diffpair_full(): if __name__ == "__main__": - test_regression_rp2040_usb_diffpair() + # test_regression_rp2040_usb_diffpair() + test_up_connect_simple_multiple()