Program Listing for File graph.hpp

Return to documentation for file (xnetwork/classes/graph.hpp)

#pragma once

#include <any>
#include <cassert>
#include <py2cpp/py2cpp.hpp>
#include <range/v3/view/enumerate.hpp>
#include <string_view>
#include <type_traits>
#include <vector>
#include <xnetwork/classes/coreviews.hpp> // import AtlasView, AdjacencyView
#include <xnetwork/classes/reportviews.hpp> // import NodeView, EdgeView, DegreeView

namespace xn
{

struct object : py::dict<const char*, std::any>
{
};

template <typename _nodeview_t,
    typename adjlist_t = py::set<Value_type<_nodeview_t>>,
    typename adjlist_outer_dict_factory =
        py::dict<Value_type<_nodeview_t>, adjlist_t>>
class Graph : public object
{
  public:
    using nodeview_t = _nodeview_t;
    using Node = typename nodeview_t::value_type; // luk
    using dict = py::dict<const char*, std::any>;
    using graph_attr_dict_factory = dict;
    // using edge_attr_dict_factory = dict;
    // using node_attr_dict_factory = dict;
    // using node_dict_factory = py::dict<Node, node_attr_dict_factory>;
    // using adjlist_inner_dict_factory = py::dict<Node,
    // edge_attr_dict_factory>;
    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<Node, Node>;
    using node_t = Node;

    size_t _num_of_edges = 0;

    // std::vector<Node > _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(uint32_t 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<decltype(this->_adj)>;
        return AdjacencyView<const T&>(this->_adj);
    }

    auto adj()
    {
        using T = std::remove_cv_t<decltype(this->_adj)>;
        return AdjacencyView<T>(this->_adj);
    }

    auto _nodes_nbrs() const
    {
        // @TODO support py:dict
        return ranges::views::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<const char*>(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<T>(*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 <typename U = key_type>
    auto add_edge(const Node& u, const Node& v) ->
        typename std::enable_if<std::is_same<U, value_type>::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 <typename U = key_type>
    auto add_edge(const Node& u, const Node& v) ->
        typename std::enable_if<!std::is_same<U, value_type>::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 <typename T>
    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 <typename C1, typename C2>
    auto add_edges_from(const C1& edges, const C2& data)
    {
        auto it = data.begin();
        for (const auto& e : edges)
        {
            this->add_edge(e.first, e.second, *it);
            ++it;
        }
    }

    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;
    // }

    // /// @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<decltype(py::range<uint32_t>(uint32_t{})), py::set<uint32_t>, std::vector<py::set<uint32_t>>>;

// template <typename nodeview_t,
//           typename adjlist_t> Graph(int )
// -> Graph<decltype(py::range(1)), py::set<int>>;

} // namespace xn