:github_url: https://github.com/svenevs/exhale-companion .. _program_listing_file_xnetwork_classes_graph.hpp: Program Listing for File graph.hpp ================================== |exhale_lsh| :ref:`Return to documentation for file ` (``xnetwork/classes/graph.hpp``) .. |exhale_lsh| unicode:: U+021B0 .. UPWARDS ARROW WITH TIP LEFTWARDS .. code-block:: cpp #pragma once #include #include #include #include #include #include #include #include // import AtlasView, AdjacencyView #include // import NodeView, EdgeView, DegreeView namespace xn { struct object : py::dict { }; template >, typename adjlist_outer_dict_factory = py::dict, adjlist_t>> class Graph : public object { public: using nodeview_t = _nodeview_t; using Node = typename nodeview_t::value_type; // luk using dict = py::dict; using graph_attr_dict_factory = dict; // using edge_attr_dict_factory = dict; // using node_attr_dict_factory = dict; // using node_dict_factory = py::dict; // using adjlist_inner_dict_factory = py::dict; using adjlist_inner_dict_factory = adjlist_t; using key_type = typename adjlist_t::key_type; using value_type = typename adjlist_t::value_type; using edge_t = std::pair; using node_t = Node; size_t _num_of_edges = 0; // std::vector _Nodes{}; nodeview_t _node; graph_attr_dict_factory graph {}; // dictionary for graph attributes // node_dict_factory _node{}; // empty node attribute dict adjlist_outer_dict_factory _adj; // empty adjacency dict // auto __getstate__() { // attr = this->__dict__.copy(); // // remove lazy property attributes // if ("nodes" : attr) { // del attr["nodes"]; // } // if ("edges" : attr) { // del attr["edges"]; // } // if ("degree" : attr) { // del attr["degree"]; // } // return attr; // } explicit Graph(const nodeview_t& Nodes) : _node {Nodes} , _adj {} // py::dict??? { } explicit Graph(int num_nodes) : _node {py::range(num_nodes)} , _adj(num_nodes) // std::vector { } // Graph(const Graph&) = delete; // don't copy // Graph& operator=(const Graph&) = delete; // don't copy // Graph(Graph&&) noexcept = default; static auto end_points(edge_t& e) -> edge_t& { return e; } static auto end_points(const edge_t& e) -> const edge_t& { return e; } auto adj() const { using T = std::remove_reference_t_adj)>; return AdjacencyView(this->_adj); } auto adj() { using T = std::remove_cv_t_adj)>; return AdjacencyView(this->_adj); } auto _nodes_nbrs() const { // @TODO support py:dict return py::enumerate(this->_adj); } // auto null_vertex() const -> const Node& // { // return *(this->_node.end()); // } // Node& null_vertex() // { // return *(this->_node.end()); // } auto get_name() { if (!this->graph.contains("name")) { return ""; } return std::any_cast(this->graph["name"]); } // @name.setter auto set_name(std::string_view s) { this->graph["name"] = std::any(s); } auto begin() const { return std::begin(this->_node); } auto end() const { return std::end(this->_node); } auto contains(const Node& n) -> bool { return this->_node.contains(n); } auto operator[](const Node& n) const -> const auto& { return this->adj()[n]; } auto operator[](const Node& n) -> auto& { return this->adj()[n]; } auto nodes() { using T = decltype(*this); auto nodes = NodeView(*this); // Lazy View creation: overload the (class) property on the instance // Then future G.nodes use the existing View // setattr doesn"t work because attribute already exists this->operator[]("nodes") = std::any(nodes); return nodes; } auto number_of_nodes() const { return this->_node.size(); } auto number_of_edges() const { return this->_num_of_edges; } auto order() { return this->_node.size(); } auto has_node(const Node& n) { return this->_node.contains(n); } template auto add_edge(const Node& u, const Node& v) -> typename std::enable_if::value>::type { // auto [u, v] = u_of_edge, v_of_edge; // add nodes // assert(this->_node.contains(u)); // assert(this->_node.contains(v)); // add the edge // datadict = this->_adj[u].get(v, this->edge_attr_dict_factory()); // datadict.update(attr); // set this->_adj[u].insert(v); this->_adj[v].insert(u); this->_num_of_edges += 1; } template auto add_edge(const Node& u, const Node& v) -> typename std::enable_if::value>::type { // auto [u, v] = u_of_edge, v_of_edge; // add nodes // assert(this->_node.contains(u)); // assert(this->_node.contains(v)); // add the edge // datadict = this->_adj[u].get(v, this->edge_attr_dict_factory()); // datadict.update(attr); using T = typename adjlist_t::mapped_type; auto data = this->_adj[u].get(v, T {}); this->_adj[u][v] = data; this->_adj[v][u] = data; // ??? this->_num_of_edges += 1; } template auto add_edge(const Node& u, const Node& v, const T& data) { // assert(this->_node.contains(u)); // assert(this->_node.contains(v)); this->_adj[u][v] = data; this->_adj[v][u] = data; this->_num_of_edges += 1; } template auto add_edges_from(const C1& edges, const C2& data) { auto N = edges.size(); for (auto i = 0U; i != N; ++i) { const auto& e = edges[i]; this->add_edge(e.first, e.second, data[i]); } } auto has_edge(const Node& u, const Node& v) -> bool { return this->_adj[u].contains(v); } auto degree(const Node& n) const { return this->_adj[n].size(); } // auto edges() { // auto edges = EdgeView(*this); // this->operator[]("edges") = std::any(edges); // return edges; // } using coro_t = boost::coroutines2::coroutine; using pull_t = typename coro_t::pull_type; auto edges() const -> pull_t { auto func = [&](typename coro_t::push_type& yield) { if constexpr (std::is_same_v(0))>) { // this->_succ??? for (auto&& [n, nbrs] : py::enumerate(this->_adj)) { for (auto&& nbr : nbrs) { yield(edge_t {Node(n), Node(nbr)}); } } } else { for (auto&& [n, nbrs] : this->_adj.items()) { for (auto&& nbr : nbrs) { yield(edge_t {n, nbr}); } } } }; return pull_t(func); } // /// @property // auto degree() { // /*! A DegreeView for the Graph as G.degree or G.degree(). // The node degree is the number of edges adjacent to the node. // The weighted node degree is the sum of the edge weights for // edges incident to that node. // This object provides an iterator for (node, degree) as well as // lookup for the degree for a single node. // Parameters // ---------- // nbunch : single node, container, or all nodes (default= all nodes); // The view will only report edges incident to these nodes. // weight : string or None, optional (default=None); // The name of an edge attribute that holds the numerical value used // as a weight. If None, then each edge has weight 1. // The degree is the sum of the edge weights adjacent to the node. // Returns // ------- // If a single node is requested // deg : int // Degree of the node // OR if (multiple nodes are requested // nd_view : A DegreeView object capable of iterating (node, degree) // pairs // Examples // -------- // >>> G = xn::path_graph(4); // or DiGraph, MultiGraph, MultiDiGraph, // etc // >>> G.degree[0]; // node 0 has degree 1 // 1 // >>> list(G.degree([0, 1, 2])); // [(0, 1), (1, 2), (2, 2)]; // */ // auto degree = DegreeView(*this); // this->operator[]("degree") = std::any(degree); // return degree; // } auto clear() { this->_adj.clear(); // this->_node.clear(); this->graph.clear(); } auto is_multigraph() { return false; } auto is_directed() { return false; } }; using SimpleGraph = Graph(1)), py::set, std::vector>>; // template Graph(int ) // -> Graph(1)), py::set>; } // namespace xn