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