123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663 |
- // Copyright (c) 2019, QuantStack and Mamba Contributors
- //
- // Distributed under the terms of the BSD 3-Clause License.
- //
- // The full license is in the file LICENSE, distributed with this software.
- #ifndef MAMBA_UTIL_GRAPH_HPP
- #define MAMBA_UTIL_GRAPH_HPP
- #include <algorithm>
- #include <functional>
- #include <iterator>
- #include <map>
- #include <utility>
- #include <vector>
- #include "flat_set.hpp"
- namespace mamba::util
- {
- // Simplified implementation of a directed graph
- template <typename Node, typename Derived>
- class DiGraphBase
- {
- public:
- using node_t = Node;
- using node_id = std::size_t;
- using node_map = std::map<node_id, node_t>;
- using node_id_list = flat_set<node_id>;
- using adjacency_list = std::vector<node_id_list>;
- node_id add_node(const node_t& value);
- node_id add_node(node_t&& value);
- bool add_edge(node_id from, node_id to);
- bool remove_edge(node_id from, node_id to);
- bool remove_node(node_id id);
- bool empty() const;
- std::size_t number_of_nodes() const noexcept;
- std::size_t number_of_edges() const noexcept;
- std::size_t in_degree(node_id id) const noexcept;
- std::size_t out_degree(node_id id) const noexcept;
- const node_map& nodes() const;
- const node_t& node(node_id id) const;
- node_t& node(node_id id);
- const node_id_list& successors(node_id id) const;
- const adjacency_list& successors() const;
- const node_id_list& predecessors(node_id id) const;
- const adjacency_list& predecessors() const;
- bool has_node(node_id id) const;
- bool has_edge(node_id from, node_id to) const;
- // TODO C++20 better to return a range since this search cannot be interupted from the
- // visitor
- template <typename UnaryFunc>
- UnaryFunc for_each_node_id(UnaryFunc func) const;
- template <typename BinaryFunc>
- BinaryFunc for_each_edge_id(BinaryFunc func) const;
- template <typename UnaryFunc>
- UnaryFunc for_each_leaf_id(UnaryFunc func) const;
- template <typename UnaryFunc>
- UnaryFunc for_each_leaf_id_from(node_id source, UnaryFunc func) const;
- template <typename UnaryFunc>
- UnaryFunc for_each_root_id(UnaryFunc func) const;
- template <typename UnaryFunc>
- UnaryFunc for_each_root_id_from(node_id source, UnaryFunc func) const;
- // TODO C++20 better to return a range since this search cannot be interupted from the
- // visitor
- template <class V>
- void depth_first_search(V& visitor, node_id start = node_id(0), bool reverse = false) const;
- protected:
- using derived_t = Derived;
- DiGraphBase() = default;
- DiGraphBase(const DiGraphBase&) = default;
- DiGraphBase(DiGraphBase&&) = default;
- DiGraphBase& operator=(const DiGraphBase&) = default;
- DiGraphBase& operator=(DiGraphBase&&) = default;
- ~DiGraphBase() = default;
- node_id number_of_node_id() const noexcept;
- Derived& derived_cast();
- const Derived& derived_cast() const;
- private:
- enum class visited
- {
- no,
- ongoing,
- yes
- };
- using visited_list = std::vector<visited>;
- template <class V>
- node_id add_node_impl(V&& value);
- template <class V>
- void depth_first_search_impl(
- V& visitor,
- node_id node,
- visited_list& status,
- const adjacency_list& successors
- ) const;
- // Source of truth for exsising nodes
- node_map m_node_map;
- // May contains empty slots after `remove_node`
- adjacency_list m_predecessors;
- // May contains empty slots after `remove_node`
- adjacency_list m_successors;
- std::size_t m_number_of_edges = 0;
- };
- template <typename Node, typename Derived>
- auto is_reachable(
- const DiGraphBase<Node, Derived>& graph,
- typename DiGraphBase<Node, Derived>::node_id source,
- typename DiGraphBase<Node, Derived>::node_id target
- ) -> bool;
- template <class G>
- class default_visitor
- {
- public:
- using graph_t = G;
- using node_id = typename graph_t::node_id;
- void start_node(node_id, const graph_t&)
- {
- }
- void finish_node(node_id, const graph_t&)
- {
- }
- void start_edge(node_id, node_id, const graph_t&)
- {
- }
- void tree_edge(node_id, node_id, const graph_t&)
- {
- }
- void back_edge(node_id, node_id, const graph_t&)
- {
- }
- void forward_or_cross_edge(node_id, node_id, const graph_t&)
- {
- }
- void finish_edge(node_id, node_id, const graph_t&)
- {
- }
- };
- template <typename Node, typename Edge = void>
- class DiGraph : private DiGraphBase<Node, DiGraph<Node, Edge>>
- {
- public:
- using Base = DiGraphBase<Node, DiGraph<Node, Edge>>;
- using typename Base::adjacency_list;
- using typename Base::node_id;
- using typename Base::node_id_list;
- using typename Base::node_map;
- using typename Base::node_t;
- using edge_t = Edge;
- using edge_id = std::pair<node_id, node_id>;
- using edge_map = std::map<edge_id, edge_t>;
- using Base::empty;
- using Base::has_edge;
- using Base::has_node;
- using Base::in_degree;
- using Base::node;
- using Base::nodes;
- using Base::number_of_edges;
- using Base::number_of_nodes;
- using Base::out_degree;
- using Base::predecessors;
- using Base::successors;
- using Base::for_each_edge_id;
- using Base::for_each_leaf_id;
- using Base::for_each_leaf_id_from;
- using Base::for_each_node_id;
- using Base::for_each_root_id;
- using Base::for_each_root_id_from;
- using Base::depth_first_search;
- using Base::add_node;
- bool add_edge(node_id from, node_id to, const edge_t& data);
- bool add_edge(node_id from, node_id to, edge_t&& data);
- bool remove_edge(node_id from, node_id to);
- bool remove_node(node_id id);
- const edge_map& edges() const;
- const edge_t& edge(node_id from, node_id to) const;
- const edge_t& edge(edge_id edge) const;
- edge_t& edge(node_id from, node_id to);
- edge_t& edge(edge_id edge);
- private:
- friend class DiGraphBase<Node, DiGraph<Node, Edge>>; // required for private CRTP
- template <typename T>
- bool add_edge_impl(node_id from, node_id to, T&& data);
- edge_map m_edges;
- };
- template <typename Node>
- class DiGraph<Node, void> : public DiGraphBase<Node, DiGraph<Node, void>>
- {
- };
- /********************************
- * DiGraphBase Implementation *
- ********************************/
- template <typename N, typename G>
- bool DiGraphBase<N, G>::empty() const
- {
- return number_of_nodes() == 0;
- }
- template <typename N, typename G>
- auto DiGraphBase<N, G>::number_of_nodes() const noexcept -> std::size_t
- {
- return m_node_map.size();
- }
- template <typename N, typename G>
- auto DiGraphBase<N, G>::number_of_edges() const noexcept -> std::size_t
- {
- return m_number_of_edges;
- }
- template <typename N, typename G>
- auto DiGraphBase<N, G>::in_degree(node_id id) const noexcept -> std::size_t
- {
- return m_predecessors[id].size();
- }
- template <typename N, typename G>
- auto DiGraphBase<N, G>::out_degree(node_id id) const noexcept -> std::size_t
- {
- return m_successors[id].size();
- }
- template <typename N, typename G>
- auto DiGraphBase<N, G>::nodes() const -> const node_map&
- {
- return m_node_map;
- }
- template <typename N, typename G>
- auto DiGraphBase<N, G>::node(node_id id) const -> const node_t&
- {
- return m_node_map.at(id);
- }
- template <typename N, typename G>
- auto DiGraphBase<N, G>::node(node_id id) -> node_t&
- {
- return m_node_map.at(id);
- }
- template <typename N, typename G>
- auto DiGraphBase<N, G>::successors(node_id id) const -> const node_id_list&
- {
- return m_successors[id];
- }
- template <typename N, typename G>
- auto DiGraphBase<N, G>::successors() const -> const adjacency_list&
- {
- return m_successors;
- }
- template <typename N, typename G>
- auto DiGraphBase<N, G>::predecessors(node_id id) const -> const node_id_list&
- {
- return m_predecessors[id];
- }
- template <typename N, typename G>
- auto DiGraphBase<N, G>::predecessors() const -> const adjacency_list&
- {
- return m_predecessors;
- }
- template <typename N, typename G>
- auto DiGraphBase<N, G>::has_node(node_id id) const -> bool
- {
- return nodes().count(id) > 0;
- }
- template <typename N, typename G>
- auto DiGraphBase<N, G>::has_edge(node_id from, node_id to) const -> bool
- {
- return has_node(from) && successors(from).contains(to);
- }
- template <typename N, typename G>
- auto DiGraphBase<N, G>::add_node(const node_t& value) -> node_id
- {
- return add_node_impl(value);
- }
- template <typename N, typename G>
- auto DiGraphBase<N, G>::add_node(node_t&& value) -> node_id
- {
- return add_node_impl(std::move(value));
- }
- template <typename N, typename G>
- template <class V>
- auto DiGraphBase<N, G>::add_node_impl(V&& value) -> node_id
- {
- const node_id id = number_of_node_id();
- m_node_map.emplace(id, std::forward<V>(value));
- m_successors.push_back(node_id_list());
- m_predecessors.push_back(node_id_list());
- return id;
- }
- template <typename N, typename G>
- bool DiGraphBase<N, G>::remove_node(node_id id)
- {
- if (!has_node(id))
- {
- return false;
- }
- const auto succs = successors(id); // Cannot iterate on object being modified
- for (const auto& to : succs)
- {
- remove_edge(id, to);
- }
- const auto preds = predecessors(id); // Cannot iterate on object being modified
- for (const auto& from : preds)
- {
- remove_edge(from, id);
- }
- m_node_map.erase(id);
- return true;
- }
- template <typename N, typename G>
- bool DiGraphBase<N, G>::add_edge(node_id from, node_id to)
- {
- if (has_edge(from, to))
- {
- return false;
- }
- m_successors[from].insert(to);
- m_predecessors[to].insert(from);
- ++m_number_of_edges;
- return true;
- }
- template <typename N, typename G>
- bool DiGraphBase<N, G>::remove_edge(node_id from, node_id to)
- {
- if (!has_edge(from, to))
- {
- return false;
- }
- m_successors[from].erase(to);
- m_predecessors[to].erase(from);
- --m_number_of_edges;
- return true;
- }
- template <typename N, typename G>
- template <typename UnaryFunc>
- UnaryFunc DiGraphBase<N, G>::for_each_node_id(UnaryFunc func) const
- {
- for (const auto& [i, _] : m_node_map)
- {
- func(i);
- }
- return func;
- }
- template <typename N, typename G>
- template <typename BinaryFunc>
- BinaryFunc DiGraphBase<N, G>::for_each_edge_id(BinaryFunc func) const
- {
- for_each_node_id(
- [&](node_id i)
- {
- for (node_id j : successors(i))
- {
- func(i, j);
- }
- }
- );
- return func;
- }
- template <typename N, typename G>
- template <typename UnaryFunc>
- UnaryFunc DiGraphBase<N, G>::for_each_leaf_id(UnaryFunc func) const
- {
- for_each_node_id(
- [&](node_id i)
- {
- if (out_degree(i) == 0)
- {
- func(i);
- }
- }
- );
- return func;
- }
- template <typename N, typename G>
- template <typename UnaryFunc>
- UnaryFunc DiGraphBase<N, G>::for_each_root_id(UnaryFunc func) const
- {
- for_each_node_id(
- [&](node_id i)
- {
- if (in_degree(i) == 0)
- {
- func(i);
- }
- }
- );
- return func;
- }
- template <typename N, typename G>
- template <typename UnaryFunc>
- UnaryFunc DiGraphBase<N, G>::for_each_leaf_id_from(node_id source, UnaryFunc func) const
- {
- struct LeafVisitor : default_visitor<derived_t>
- {
- UnaryFunc& m_func;
- LeafVisitor(UnaryFunc& func)
- : m_func{ func }
- {
- }
- void start_node(node_id n, const derived_t& g)
- {
- if (g.out_degree(n) == 0)
- {
- m_func(n);
- }
- }
- };
- auto visitor = LeafVisitor(func);
- depth_first_search(visitor, source);
- return func;
- }
- template <typename N, typename G>
- template <typename UnaryFunc>
- UnaryFunc DiGraphBase<N, G>::for_each_root_id_from(node_id source, UnaryFunc func) const
- {
- struct RootVisitor : default_visitor<derived_t>
- {
- UnaryFunc& m_func;
- RootVisitor(UnaryFunc& func)
- : m_func{ func }
- {
- }
- void start_node(node_id n, const derived_t& g)
- {
- if (g.in_degree(n) == 0)
- {
- m_func(n);
- }
- }
- };
- auto visitor = RootVisitor(func);
- depth_first_search(visitor, source, /*reverse*/ true);
- return func;
- }
- template <typename N, typename G>
- template <class V>
- void DiGraphBase<N, G>::depth_first_search(V& visitor, node_id node, bool reverse) const
- {
- if (!empty())
- {
- visited_list status(number_of_node_id(), visited::no);
- depth_first_search_impl(visitor, node, status, reverse ? m_predecessors : m_successors);
- }
- }
- template <typename N, typename G>
- template <class V>
- void DiGraphBase<N, G>::depth_first_search_impl(
- V& visitor,
- node_id node,
- visited_list& status,
- const adjacency_list& successors
- ) const
- {
- status[node] = visited::ongoing;
- visitor.start_node(node, derived_cast());
- for (auto child : successors[node])
- {
- visitor.start_edge(node, child, derived_cast());
- if (status[child] == visited::no)
- {
- visitor.tree_edge(node, child, derived_cast());
- depth_first_search_impl(visitor, child, status, successors);
- }
- else if (status[child] == visited::ongoing)
- {
- visitor.back_edge(node, child, derived_cast());
- }
- else
- {
- visitor.forward_or_cross_edge(node, child, derived_cast());
- }
- visitor.finish_edge(node, child, derived_cast());
- }
- status[node] = visited::yes;
- visitor.finish_node(node, derived_cast());
- }
- template <typename N, typename G>
- auto DiGraphBase<N, G>::number_of_node_id() const noexcept -> node_id
- {
- // Not number_of_nodes because due to remove nodes it may be larger
- return m_successors.size();
- }
- template <typename N, typename G>
- auto DiGraphBase<N, G>::derived_cast() -> derived_t&
- {
- return static_cast<derived_t&>(*this);
- }
- template <typename N, typename G>
- auto DiGraphBase<N, G>::derived_cast() const -> const derived_t&
- {
- return static_cast<const derived_t&>(*this);
- }
- /*******************************
- * Algorithms implementation *
- *******************************/
- template <typename Graph>
- auto
- is_reachable(const Graph& graph, typename Graph::node_id source, typename Graph::node_id target)
- -> bool
- {
- struct : default_visitor<Graph>
- {
- using node_id = typename Graph::node_id;
- node_id target;
- bool target_visited = false;
- void start_node(node_id node, const Graph&)
- {
- target_visited = target_visited || (node == target);
- }
- } visitor{ {}, target };
- graph.depth_first_search(visitor, source);
- return visitor.target_visited;
- }
- /*********************************
- * DiGraph Edge Implementation *
- *********************************/
- template <typename N, typename E>
- bool DiGraph<N, E>::add_edge(node_id from, node_id to, const edge_t& data)
- {
- return add_edge_impl(from, to, data);
- }
- template <typename N, typename E>
- bool DiGraph<N, E>::add_edge(node_id from, node_id to, edge_t&& data)
- {
- return add_edge_impl(from, to, std::move(data));
- }
- template <typename N, typename E>
- template <typename T>
- bool DiGraph<N, E>::add_edge_impl(node_id from, node_id to, T&& data)
- {
- if (const bool added = Base::add_edge(from, to); added)
- {
- auto l_edge_id = std::pair(from, to);
- m_edges.insert(std::pair(l_edge_id, std::forward<T>(data)));
- return true;
- }
- return false;
- }
- template <typename N, typename E>
- bool DiGraph<N, E>::remove_edge(node_id from, node_id to)
- {
- m_edges.erase({ from, to }); // No-op if edge does not exists
- return Base::remove_edge(from, to);
- }
- template <typename N, typename E>
- bool DiGraph<N, E>::remove_node(node_id id)
- {
- // No-op if edge does not exists
- for (const auto& to : successors(id))
- {
- m_edges.erase({ id, to });
- }
- for (const auto& from : predecessors(id))
- {
- m_edges.erase({ from, id });
- }
- return Base::remove_node(id);
- }
- template <typename N, typename E>
- auto DiGraph<N, E>::edges() const -> const edge_map&
- {
- return m_edges;
- }
- template <typename N, typename E>
- auto DiGraph<N, E>::edge(edge_id edge) const -> const edge_t&
- {
- return m_edges.at(edge);
- }
- template <typename N, typename E>
- auto DiGraph<N, E>::edge(node_id from, node_id to) const -> const edge_t&
- {
- return edge({ from, to });
- }
- template <typename N, typename E>
- auto DiGraph<N, E>::edge(edge_id edge) -> edge_t&
- {
- return m_edges[edge];
- }
- template <typename N, typename E>
- auto DiGraph<N, E>::edge(node_id from, node_id to) -> edge_t&
- {
- return edge({ from, to });
- }
- }
- #endif
|