#include "directed_graph.h" #include "../common/utils.h" DiGraph::DiGraph() : Graph() { } py::object DiGraph__init__(py::args args, py::kwargs kwargs) { py::object self = args[0]; self.attr("__init__")(); DiGraph& self_ = self.cast(); py::dict graph_attr = kwargs; self_.graph.attr("update")(graph_attr); self_.nodes_cache = py::dict(); self_.adj_cache = py::dict(); return py::none(); } py::object DiGraph_out_degree(py::object self, py::object weight) { py::dict degree = py::dict(); py::list edges = self.attr("edges").cast(); py::object u, v; py::dict d; for (int i = 0; i < py::len(edges); i++) { py::tuple edge = edges[i].cast(); u = edge[0]; v = edge[1]; d = edge[2].cast(); if (degree.contains(u)) { degree[u] = py::object(degree[u]) + d.attr("get")(weight, 1); } else { degree[u] = d.attr("get")(weight, 1); } } py::list nodes = py::list(self.attr("nodes")); for (int i = 0; i < py::len(nodes); i++) { py::object node = nodes[i]; if (!degree.contains(node)) { degree[node] = 0; } } return degree; } py::object DiGraph_in_degree(py::object self, py::object weight) { py::dict degree = py::dict(); py::list edges = self.attr("edges").cast(); py::object u, v; py::dict d; for (int i = 0; i < py::len(edges); i++) { py::tuple edge = edges[i].cast(); u = edge[0]; v = edge[1]; d = edge[2].cast(); if (degree.contains(v)) { degree[v] = py::object(degree[v]) + d.attr("get")(weight, 1); } else { degree[v] = d.attr("get")(weight, 1); } } py::list nodes = py::list(self.attr("nodes")); for (int i = 0; i < py::len(nodes); i++) { py::object node = nodes[i]; if (!degree.contains(node)) { degree[node] = 0; } } return degree; } py::object DiGraph_degree(py::object self, py::object weight) { py::dict degree = py::dict(); py::dict out_degree = self.attr("out_degree")(weight).cast(); py::dict in_degree = self.attr("in_degree")(weight).cast(); py::list nodes = py::list(self.attr("nodes")); for (int i = 0; i < py::len(nodes); i++) { py::object u = nodes[i]; degree[u] = out_degree[u] + in_degree[u]; } return degree; } py::object DiGraph_size(py::object self, py::object weight) { py::dict out_degree = self.attr("out_degree")(weight).cast(); py::object s = py_sum(out_degree.attr("values")()); return (weight.is_none()) ? py::int_(s) : s; } py::object DiGraph_number_of_edges(py::object self, py::object u, py::object v) { if (u.is_none()) { return self.attr("size")(); } Graph& G = self.cast(); node_t u_id = G.node_to_id[u].cast(); node_t v_id = G.node_to_id.attr("get")(v, -1).cast(); return py::cast(int(v_id != -1 && G.adj[u_id].count(v_id))); } py::object DiGraph_neighbors(py::object self, py::object node) { Graph& self_ = self.cast(); if (self_.node_to_id.contains(node)) { return self.attr("adj")[node].attr("__iter__")(); } else { PyErr_Format(PyExc_KeyError, "No node %R", node.ptr()); return py::none(); } } py::object DiGraph_predecessors(py::object self, py::object node) { DiGraph& self_ = self.cast(); adj_dict_factory& pred = self_.pred; node_t node_id = self_.node_to_id[node].cast(); if (pred.find(node_id) != pred.end()) { adj_attr_dict_factory node_pred_dict = pred[node_id]; py::dict node_pred = py::dict(); for (adj_attr_dict_factory::iterator i = node_pred_dict.begin(); i != node_pred_dict.end(); i++) { edge_attr_dict_factory edge_attr_dict = i->second; node_pred[self_.id_to_node[py::cast(i->first)]] = attr_to_dict(edge_attr_dict); } return node_pred.attr("__iter__")(); } else { PyErr_Format(PyExc_KeyError, "No node %R", node.ptr()); return py::none(); } } node_t DiGraph_add_one_node(DiGraph& self, py::object one_node_for_adding, py::object node_attr = py::dict()) { node_t id; if (self.node_to_id.contains(one_node_for_adding)) { id = self.node_to_id[one_node_for_adding].cast(); } else { id = ++(self.id); self.id_to_node[py::cast(id)] = one_node_for_adding; self.node_to_id[one_node_for_adding] = id; } py::list items = py::list(node_attr.attr("items")()); self.node[id] = node_attr_dict_factory(); self.adj[id] = adj_attr_dict_factory(); self.pred[id] = adj_attr_dict_factory(); for (int i = 0; i < len(items); i++) { py::tuple kv = items[i].cast(); py::object pkey = kv[0]; std::string weight_key = weight_to_string(pkey); weight_t value = kv[1].cast(); self.node[id].insert(std::make_pair(weight_key, value)); } return id; } py::object DiGraph_add_node(py::args args, py::kwargs kwargs) { DiGraph& self = args[0].cast(); self.dirty_nodes = true; self.dirty_adj = true; py::object one_node_for_adding = args[1]; py::dict node_attr = kwargs; DiGraph_add_one_node(self, one_node_for_adding, node_attr); return py::none(); } py::object DiGraph_add_nodes(DiGraph& self, py::list nodes_for_adding, py::list nodes_attr) { self.dirty_nodes = true; self.dirty_adj = true; if (py::len(nodes_attr) != 0) { if (py::len(nodes_for_adding) != py::len(nodes_attr)) { PyErr_Format(PyExc_AssertionError, "Nodes and Attributes lists must have same length."); return py::none(); } } for (int i = 0; i < py::len(nodes_for_adding); i++) { py::object one_node_for_adding = nodes_for_adding[i]; py::dict node_attr; if (py::len(nodes_attr)) { node_attr = nodes_attr[i].cast(); } else { node_attr = py::dict(); } DiGraph_add_one_node(self, one_node_for_adding, node_attr); } return py::none(); } py::object DiGraph_add_nodes_from(py::args args, py::kwargs kwargs) { DiGraph& self = args[0].cast(); self.dirty_nodes = true; self.dirty_adj = true; py::list nodes_for_adding = py::list(args[1]); for (int i = 0; i < py::len(nodes_for_adding); i++) { bool newnode; py::dict attr = kwargs; py::dict newdict, ndict; py::object n = nodes_for_adding[i]; try { newnode = !self.node_to_id.contains(n); newdict = attr; } catch (const py::error_already_set&) { PyObject *type, *value, *traceback; PyErr_Fetch(&type, &value, &traceback); if (PyErr_GivenExceptionMatches(PyExc_TypeError, type)) { py::tuple n_pair = n.cast(); n = n_pair[0]; ndict = n_pair[1].cast(); newnode = !self.node_to_id.contains(n); newdict = attr.attr("copy")(); newdict.attr("update")(ndict); } else { PyErr_Restore(type, value, traceback); return py::none(); } } if (newnode) { if (n.is_none()) { PyErr_Format(PyExc_ValueError, "None cannot be a node"); return py::none(); } DiGraph_add_one_node(self, n); } node_t id = self.node_to_id[n].cast(); py::list items = py::list(newdict.attr("items")()); for (int i = 0; i < len(items); i++) { py::tuple kv = items[i].cast(); py::object pkey = kv[0]; std::string weight_key = weight_to_string(pkey); weight_t value = kv[1].cast(); self.node[id].insert(std::make_pair(weight_key, value)); } } return py::none(); } py::object DiGraph_remove_node(DiGraph& self, py::object node_to_remove) { self.dirty_nodes = true; self.dirty_adj = true; if (!self.node_to_id.contains(node_to_remove)) { PyErr_Format(PyExc_KeyError, "No node %R in graph.", node_to_remove.ptr()); return py::none(); } node_t node_to_remove_id = self.node_to_id[node_to_remove].cast(); adj_attr_dict_factory succs = self.adj[node_to_remove_id]; adj_attr_dict_factory preds = self.pred[node_to_remove_id]; self.node.erase(node_to_remove_id); for (adj_attr_dict_factory::iterator i = succs.begin(); i != succs.end(); i++) { self.pred[i->first].erase(node_to_remove_id); } for (adj_attr_dict_factory::iterator i = preds.begin(); i != preds.end(); i++) { self.adj[i->first].erase(node_to_remove_id); } self.adj.erase(node_to_remove_id); self.pred.erase(node_to_remove_id); self.node_to_id.attr("pop")(node_to_remove); self.id_to_node.attr("pop")(node_to_remove_id); return py::none(); } py::object DiGraph_remove_nodes(py::object self, py::list nodes_to_remove) { DiGraph& self_ = self.cast(); self_.dirty_nodes = true; self_.dirty_adj = true; for (int i = 0; i < py::len(nodes_to_remove); i++) { py::object node_to_remove = nodes_to_remove[i]; if (!self_.node_to_id.contains(node_to_remove)) { PyErr_Format(PyExc_KeyError, "No node %R in graph.", node_to_remove.ptr()); return py::none(); } } for (int i = 0; i < py::len(nodes_to_remove); i++) { py::object node_to_remove = nodes_to_remove[i]; self.attr("remove_node")(node_to_remove); } return py::none(); } void DiGraph_add_one_edge(DiGraph& self, py::object u_of_edge, py::object v_of_edge, py::object edge_attr) { node_t u, v; if (!self.node_to_id.contains(u_of_edge)) { u = DiGraph_add_one_node(self, u_of_edge); } else { u = self.node_to_id[u_of_edge].cast(); } if (!self.node_to_id.contains(v_of_edge)) { v = DiGraph_add_one_node(self, v_of_edge); } else { v = self.node_to_id[v_of_edge].cast(); } py::list items = py::list(edge_attr.attr("items")()); self.adj[u][v] = node_attr_dict_factory(); self.pred[v][u] = edge_attr_dict_factory(); for (int i = 0; i < len(items); i++) { py::tuple kv = items[i].cast(); py::object pkey = kv[0]; std::string weight_key = weight_to_string(pkey); weight_t value = kv[1].cast(); self.adj[u][v].insert(std::make_pair(weight_key, value)); self.pred[v][u].insert(std::make_pair(weight_key, value)); } } py::object DiGraph_add_edge(py::args args, py::kwargs kwargs) { DiGraph& self = args[0].cast(); self.dirty_nodes = true; self.dirty_adj = true; py::object u_of_edge = args[1], v_of_edge = args[2]; py::dict edge_attr = kwargs; DiGraph_add_one_edge(self, u_of_edge, v_of_edge, edge_attr); return py::none(); } py::object DiGraph_add_edges(DiGraph& self, py::list edges_for_adding, py::list edges_attr) { self.dirty_nodes = true; self.dirty_adj = true; if (py::len(edges_attr) != 0) { if (py::len(edges_for_adding) != py::len(edges_attr)) { PyErr_Format(PyExc_AssertionError, "Edges and Attributes lists must have same length."); return py::none(); } } for (int i = 0; i < py::len(edges_for_adding); i++) { py::tuple one_edge_for_adding = edges_for_adding[i].cast(); py::dict edge_attr; if (py::len(edges_attr)) { edge_attr = edges_attr[i].cast(); } else { edge_attr = py::dict(); } DiGraph_add_one_edge(self, one_edge_for_adding[0], one_edge_for_adding[1], edge_attr); } return py::none(); } py::object DiGraph_add_edges_from(py::args args, py::kwargs attr) { DiGraph& self = args[0].cast(); self.dirty_nodes = true; self.dirty_adj = true; py::list ebunch_to_add = py::list(args[1]); for (int i = 0; i < len(ebunch_to_add); i++) { py::list e = py::list(ebunch_to_add[i]); py::object u, v; py::dict dd; switch (len(e)) { case 2: { u = e[0]; v = e[1]; break; } case 3: { u = e[0]; v = e[1]; dd = e[2].cast(); break; } default: { PyErr_Format(PyExc_ValueError, "Edge tuple %R must be a 2 - tuple or 3 - tuple.", e.ptr()); return py::none(); } } node_t u_id, v_id; if (!self.node_to_id.contains(u)) { if (u.is_none()) { PyErr_Format(PyExc_ValueError, "None cannot be a node"); return py::none(); } u_id = DiGraph_add_one_node(self, u); } else { u_id = self.node_to_id[u].cast(); } if (!self.node_to_id.contains(v)) { if (v.is_none()) { PyErr_Format(PyExc_ValueError, "None cannot be a node"); return py::none(); } v_id = DiGraph_add_one_node(self, v); } else { v_id = self.node_to_id[v].cast(); } auto datadict = self.adj[u_id].count(v_id) ? self.adj[u_id][v_id] : node_attr_dict_factory(); py::list items = py::list(attr.attr("items")()); items.attr("extend")(py::list(dd.attr("items")())); for (int j = 0; j < py::len(items); j++) { py::tuple kv = items[j].cast(); py::object pkey = kv[0]; std::string weight_key = weight_to_string(pkey); weight_t value = kv[1].cast(); datadict.insert(std::make_pair(weight_key, value)); } // Warning: in Graph.py the edge attr is directed assigned by the dict extended from the original attr self.adj[u_id][v_id].insert(datadict.begin(), datadict.end()); } return py::none(); } py::object DiGraph_add_edges_from_file(DiGraph& self, py::str file, py::object weighted, py::object is_transform) { self.dirty_nodes = true; self.dirty_adj = true; bool _is_transform = is_transform.cast(); struct commactype : std::ctype { commactype() : std::ctype(get_table()) {} std::ctype_base::mask const* get_table() { std::ctype_base::mask* rc = 0; if (rc == 0) { rc = new std::ctype_base::mask[std::ctype::table_size]; std::fill_n(rc, std::ctype::table_size, std::ctype_base::mask()); rc[','] = std::ctype_base::space; rc[' '] = std::ctype_base::space; rc['\t'] = std::ctype_base::space; rc['\n'] = std::ctype_base::space; rc['\r'] = std::ctype_base::space; } return rc; } }; std::ios::sync_with_stdio(0); std::string file_path = file.cast(); std::ifstream in; in.open(file_path); if (!in.is_open()) { PyErr_Format(PyExc_FileNotFoundError, "Please check the file and make sure the path only contains English"); return py::none(); } in.imbue(std::locale(std::locale(), new commactype)); std::string data, key("weight"); std::string su, sv; weight_t weight; while (in >> su >> sv) { py::str pu(su), pv(sv); node_t u, v; if (!self.node_to_id.contains(pu)) { u = DiGraph_add_one_node(self, pu); } else { u = self.node_to_id[pu].cast(); } if (!self.node_to_id.contains(pv)) { v = DiGraph_add_one_node(self, pv); } else { v = self.node_to_id[pv].cast(); } if (weighted.cast()) { in >> weight; self.adj[u][v][key] = weight; self.pred[v][u][key] = weight; } else { if (!self.adj[u].count(v)) { self.adj[u][v] = node_attr_dict_factory(); self.pred[v][u] = node_attr_dict_factory(); } } } if(_is_transform){ Graph_L g_l = graph_to_linkgraph(self,true, key, true, false); self.linkgraph_structure = g_l; self.linkgraph_dirty = false; } in.close(); return py::none(); } py::object DiGraph_add_weighted_edge(DiGraph& self, py::object u_of_edge, py::object v_of_edge, weight_t weight) { self.dirty_nodes = true; self.dirty_adj = true; py::dict edge_attr; edge_attr["weight"] = weight; DiGraph_add_one_edge(self, u_of_edge, v_of_edge, edge_attr); return py::none(); } py::object DiGraph_remove_edge(DiGraph& self, py::object u, py::object v) { self.dirty_nodes = true; self.dirty_adj = true; if (self.node_to_id.contains(u) && self.node_to_id.contains(v)) { node_t u_id = self.node_to_id[u].cast(); node_t v_id = self.node_to_id[v].cast(); auto& u_neighbors_info = self.adj[u_id]; if (u_neighbors_info.find(v_id) != u_neighbors_info.end()) { u_neighbors_info.erase(v_id); auto& v_predecessors_info = self.pred[v_id]; v_predecessors_info.erase(u_id); return py::none(); } } PyErr_Format(PyExc_KeyError, "No edge %R-%R in graph.", u.ptr(), v.ptr()); return py::none(); } py::object DiGraph_remove_edges(py::object self, py::list edges_to_remove) { DiGraph& self_ = self.cast(); for (int i = 0; i < py::len(edges_to_remove); i++) { py::tuple edge = edges_to_remove[i].cast(); py::object u = edge[0], v = edge[1]; self.attr("remove_edge")(u, v); } self_.dirty_nodes = true; self_.dirty_adj = true; return py::none(); } py::object DiGraph_remove_edges_from(py::object self, py::list ebunch) { DiGraph& self_ = self.cast(); for (int i = 0; i < py::len(ebunch); i++) { py::tuple edge = ebunch[i].cast(); node_t u_id = edge[0].cast(); node_t v_id = edge[1].cast(); if (self_.adj[u_id].find(v_id) != self_.adj[u_id].end() && self_.adj[v_id].find(u_id) != self_.adj[v_id].end()) { self_.adj[u_id].erase(v_id); self_.pred[v_id].erase(u_id); } } return py::none(); } py::object DiGraph_nodes_subgraph(py::object self, py::list from_nodes) { py::object G = self.attr("__class__")(); Graph& self_ = self.cast(); DiGraph& G_ = G.cast(); G_.graph.attr("update")(self_.graph); py::object nodes = self.attr("nodes"); py::object adj = self.attr("adj"); for (int i = 0; i < py::len(from_nodes); i++) { py::object node = from_nodes[i]; if (self_.node_to_id.contains(node)) { py::object node_attr = nodes[node]; DiGraph_add_one_node(G_, node, node_attr); } py::object out_edges = adj[node]; py::list edge_items = py::list(out_edges.attr("items")()); for (int j = 0; j < py::len(edge_items); j++) { py::tuple item = edge_items[j].cast(); py::object v = item[0]; py::object edge_attr = item[1]; if (from_nodes.contains(v)) { DiGraph_add_one_edge(G_, node, v, edge_attr); } } } return G; } py::object DiGraph_generate_linkgraph(py::object self, py::object weight){ DiGraph& G_ = self.cast(); std::string w = weight_to_string(weight); Graph_L g_l = graph_to_linkgraph(G_, true, w, true, false); G_.linkgraph_dirty = false; G_.linkgraph_structure = g_l; return py::none(); } py::object DiGraph_copy(py::object self) { DiGraph& self_ = self.cast(); py::object G = self.attr("__class__")(); DiGraph& G_ = G.cast(); G_.graph.attr("update")(self_.graph); G_.id_to_node.attr("update")(self_.id_to_node); G_.node_to_id.attr("update")(self_.node_to_id); G_.node = self_.node; G_.adj = self_.adj; G_.pred = self_.pred; return py::object(G); } py::object DiGraph_is_directed(py::object self) { return py::cast(true); } py::object DiGraph_py(py::object self) { py::object G = py::module_::import("easygraph").attr("DiGraph")(); G.attr("graph").attr("update")(self.attr("graph")); G.attr("adj").attr("update")(self.attr("adj")); G.attr("nodes").attr("update")(self.attr("nodes")); G.attr("pred").attr("update")(self.attr("pred")); // G.attr("succ").attr("update")(self.attr("succ")); return G; } py::object DiGraph::get_pred() { adj_dict_factory pred = this->pred; py::dict predecessors = py::dict(); for (const auto& ego_edges : this->pred) { node_t start_point = ego_edges.first; py::dict ego_edges_dict = py::dict(); for (const auto& edge_info : ego_edges.second) { node_t end_point = edge_info.first; const auto& edge_attr = edge_info.second; ego_edges_dict[this->id_to_node[py::cast(end_point)]] = attr_to_dict(edge_attr); } predecessors[this->id_to_node[py::cast(start_point)]] = ego_edges_dict; } return predecessors; } py::object DiGraph::get_edges() { py::list edges = py::list(); std::set > seen; for (const auto& ego_edges : this->adj) { node_t u = ego_edges.first; for (const auto& edge_info : ego_edges.second) { node_t v = edge_info.first; const auto& edge_attr = edge_info.second; if (seen.find(std::make_pair(u, v)) == seen.end()) { seen.insert(std::make_pair(u, v)); edges.append(py::make_tuple(this->id_to_node[py::cast(u)], this->id_to_node[py::cast(v)], attr_to_dict(edge_attr))); } } } return edges; }