chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "graph.h"
|
||||
#include "directed_graph.h"
|
||||
#include "operation.h"
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "../common/common.h"
|
||||
|
||||
struct COOGraph {
|
||||
std::vector<int> row;
|
||||
std::vector<int> col;
|
||||
std::vector<double> unweighted_W;
|
||||
std::unordered_map<std::string, std::shared_ptr<std::vector<double>>> W_map;
|
||||
|
||||
std::vector<node_t> nodes;
|
||||
std::unordered_map<node_t, int> node2idx;
|
||||
};
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "../common/common.h"
|
||||
|
||||
struct CSRGraph {
|
||||
std::vector<int> V;
|
||||
std::vector<int> E;
|
||||
std::vector<double> unweighted_W;
|
||||
std::unordered_map<std::string, std::shared_ptr<std::vector<double>>> W_map;
|
||||
|
||||
std::vector<node_t> nodes;
|
||||
std::unordered_map<node_t, int> node2idx;
|
||||
};
|
||||
@@ -0,0 +1,613 @@
|
||||
#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<DiGraph&>();
|
||||
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::list>();
|
||||
py::object u, v;
|
||||
py::dict d;
|
||||
for (int i = 0; i < py::len(edges); i++) {
|
||||
py::tuple edge = edges[i].cast<py::tuple>();
|
||||
u = edge[0];
|
||||
v = edge[1];
|
||||
d = edge[2].cast<py::dict>();
|
||||
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::list>();
|
||||
py::object u, v;
|
||||
py::dict d;
|
||||
for (int i = 0; i < py::len(edges); i++) {
|
||||
py::tuple edge = edges[i].cast<py::tuple>();
|
||||
u = edge[0];
|
||||
v = edge[1];
|
||||
d = edge[2].cast<py::dict>();
|
||||
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>();
|
||||
py::dict in_degree = self.attr("in_degree")(weight).cast<py::dict>();
|
||||
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::dict>();
|
||||
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<Graph&>();
|
||||
node_t u_id = G.node_to_id[u].cast<node_t>();
|
||||
node_t v_id = G.node_to_id.attr("get")(v, -1).cast<node_t>();
|
||||
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<Graph&>();
|
||||
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<DiGraph&>();
|
||||
adj_dict_factory& pred = self_.pred;
|
||||
node_t node_id = self_.node_to_id[node].cast<node_t>();
|
||||
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<node_t>();
|
||||
} 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::tuple>();
|
||||
py::object pkey = kv[0];
|
||||
std::string weight_key = weight_to_string(pkey);
|
||||
weight_t value = kv[1].cast<weight_t>();
|
||||
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<DiGraph&>();
|
||||
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<py::dict>();
|
||||
} 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<DiGraph&>();
|
||||
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<py::tuple>();
|
||||
n = n_pair[0];
|
||||
ndict = n_pair[1].cast<py::dict>();
|
||||
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<node_t>();
|
||||
py::list items = py::list(newdict.attr("items")());
|
||||
for (int i = 0; i < len(items); i++) {
|
||||
py::tuple kv = items[i].cast<py::tuple>();
|
||||
py::object pkey = kv[0];
|
||||
std::string weight_key = weight_to_string(pkey);
|
||||
weight_t value = kv[1].cast<weight_t>();
|
||||
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<node_t>();
|
||||
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<DiGraph&>();
|
||||
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<node_t>();
|
||||
}
|
||||
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<node_t>();
|
||||
}
|
||||
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::tuple>();
|
||||
py::object pkey = kv[0];
|
||||
std::string weight_key = weight_to_string(pkey);
|
||||
weight_t value = kv[1].cast<weight_t>();
|
||||
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<DiGraph&>();
|
||||
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::tuple>();
|
||||
py::dict edge_attr;
|
||||
if (py::len(edges_attr)) {
|
||||
edge_attr = edges_attr[i].cast<py::dict>();
|
||||
} 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<DiGraph&>();
|
||||
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<py::dict>();
|
||||
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<node_t>();
|
||||
}
|
||||
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<node_t>();
|
||||
}
|
||||
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::tuple>();
|
||||
py::object pkey = kv[0];
|
||||
std::string weight_key = weight_to_string(pkey);
|
||||
weight_t value = kv[1].cast<weight_t>();
|
||||
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<bool>();
|
||||
struct commactype : std::ctype<char> {
|
||||
commactype() : std::ctype<char>(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<char>::table_size];
|
||||
std::fill_n(rc, std::ctype<char>::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::string>();
|
||||
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<node_t>();
|
||||
}
|
||||
if (!self.node_to_id.contains(pv)) {
|
||||
v = DiGraph_add_one_node(self, pv);
|
||||
} else {
|
||||
v = self.node_to_id[pv].cast<node_t>();
|
||||
}
|
||||
if (weighted.cast<bool>()) {
|
||||
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>();
|
||||
node_t v_id = self.node_to_id[v].cast<node_t>();
|
||||
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<DiGraph&>();
|
||||
for (int i = 0; i < py::len(edges_to_remove); i++) {
|
||||
py::tuple edge = edges_to_remove[i].cast<py::tuple>();
|
||||
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<DiGraph&>();
|
||||
for (int i = 0; i < py::len(ebunch); i++) {
|
||||
py::tuple edge = ebunch[i].cast<py::tuple>();
|
||||
node_t u_id = edge[0].cast<node_t>();
|
||||
node_t v_id = edge[1].cast<node_t>();
|
||||
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<Graph&>();
|
||||
DiGraph& G_ = G.cast<DiGraph&>();
|
||||
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::tuple>();
|
||||
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<DiGraph&>();
|
||||
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<DiGraph&>();
|
||||
py::object G = self.attr("__class__")();
|
||||
DiGraph& G_ = G.cast<DiGraph&>();
|
||||
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<std::pair<node_t, node_t> > 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;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include "../common/common.h"
|
||||
#include "graph.h"
|
||||
|
||||
struct DiGraph : public Graph {
|
||||
DiGraph();
|
||||
py::object get_edges();
|
||||
py::object get_pred();
|
||||
|
||||
adj_dict_factory pred;
|
||||
};
|
||||
|
||||
py::object DiGraph__init__(py::args args, py::kwargs kwargs);
|
||||
py::object DiGraph_out_degree(py::object self, py::object weight);
|
||||
py::object DiGraph_in_degree(py::object self, py::object weight);
|
||||
py::object DiGraph_degree(py::object self, py::object weight);
|
||||
py::object DiGraph_size(py::object self, py::object weight);
|
||||
py::object DiGraph_number_of_edges(py::object self, py::object u, py::object v);
|
||||
py::object DiGraph_predecessors(py::object self, py::object node);
|
||||
py::object DiGraph_add_node(py::args args, py::kwargs kwargs);
|
||||
py::object DiGraph_add_nodes(DiGraph& self, py::list nodes_for_adding, py::list nodes_attr);
|
||||
py::object DiGraph_add_nodes_from(py::args args, py::kwargs kwargs);
|
||||
py::object DiGraph_remove_node(DiGraph& self, py::object node_to_remove);
|
||||
py::object DiGraph_remove_nodes(py::object self, py::list nodes_to_remove);
|
||||
py::object DiGraph_add_edge(py::args args, py::kwargs kwargs);
|
||||
py::object DiGraph_add_edges(DiGraph& self, py::list edges_for_adding, py::list edges_attr);
|
||||
py::object DiGraph_add_edges_from_file(DiGraph& self, py::str file, py::object weighted, py::object is_transform);
|
||||
py::object DiGraph_add_edges_from(py::args args, py::kwargs attr);
|
||||
py::object DiGraph_remove_edge(DiGraph& self, py::object u, py::object v);
|
||||
py::object DiGraph_remove_edges(py::object self, py::list edges_to_remove);
|
||||
py::object DiGraph_remove_edges_from(py::object self, py::list ebunch);
|
||||
py::object DiGraph_add_weighted_edge(DiGraph& self, py::object u_of_edge, py::object v_of_edge, weight_t weight);
|
||||
py::object DiGraph_nodes_subgraph(py::object self, py::list from_nodes);
|
||||
py::object DiGraph_is_directed(py::object self);
|
||||
py::object DiGraph_py(py::object self);
|
||||
py::object DiGraph_generate_linkgraph(py::object self, py::object weight);
|
||||
@@ -0,0 +1,990 @@
|
||||
#include "graph.h"
|
||||
#include "linkgraph.h"
|
||||
#include "../common/utils.h"
|
||||
|
||||
|
||||
Graph::Graph() {
|
||||
this->id = 0;
|
||||
this->dirty_nodes = true;
|
||||
this->dirty_adj = true;
|
||||
this->linkgraph_dirty = true;
|
||||
this->csr_graph = nullptr;
|
||||
this->node_to_id = py::dict();
|
||||
this->id_to_node = py::dict();
|
||||
this->graph = py::dict();
|
||||
this->nodes_cache = py::dict();
|
||||
this->adj_cache = py::dict();
|
||||
this->coo_graph = nullptr;
|
||||
}
|
||||
|
||||
py::object Graph__init__(py::args args, py::kwargs kwargs) {
|
||||
py::object self = args[0];
|
||||
self.attr("__init__")();
|
||||
Graph& self_ = self.cast<Graph&>();
|
||||
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 Graph__iter__(py::object self) {
|
||||
return self.attr("nodes").attr("__iter__")();
|
||||
}
|
||||
|
||||
py::object Graph__len__(py::object self) {
|
||||
Graph& self_ = self.cast<Graph&>();
|
||||
return py::cast(py::len(self_.node_to_id));
|
||||
}
|
||||
|
||||
py::object Graph__contains__(py::object self, py::object node) {
|
||||
Graph& self_ = self.cast<Graph&>();
|
||||
try {
|
||||
return py::cast(self_.node_to_id.contains(node));
|
||||
} catch (const py::error_already_set&) {
|
||||
PyObject *type, *value, *traceback;
|
||||
PyErr_Fetch(&type, &value, &traceback);
|
||||
if (PyErr_GivenExceptionMatches(PyExc_TypeError, type)) {
|
||||
return py::cast(false);
|
||||
} else {
|
||||
PyErr_Restore(type, value, traceback);
|
||||
return py::none();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
py::object Graph__getitem__(py::object self, py::object node) {
|
||||
return self.attr("adj")[node];
|
||||
}
|
||||
|
||||
node_t _add_one_node(Graph& 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<node_t>();
|
||||
} 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();
|
||||
for (int i = 0; i < len(items); i++) {
|
||||
py::tuple kv = items[i].cast<py::tuple>();
|
||||
py::object pkey = kv[0];
|
||||
std::string weight_key = weight_to_string(pkey);
|
||||
weight_t value = kv[1].cast<weight_t>();
|
||||
self.node[id].insert(std::make_pair(weight_key, value));
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
py::object Graph_add_node(py::args args, py::kwargs kwargs) {
|
||||
Graph& self = args[0].cast<Graph&>();
|
||||
self.drop_cache();
|
||||
py::object one_node_for_adding = args[1];
|
||||
py::dict node_attr = kwargs;
|
||||
_add_one_node(self, one_node_for_adding, node_attr);
|
||||
return py::none();
|
||||
}
|
||||
|
||||
py::object Graph_add_nodes(Graph& self, py::list nodes_for_adding, py::list nodes_attr) {
|
||||
self.drop_cache();
|
||||
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<py::dict>();
|
||||
} else {
|
||||
node_attr = py::dict();
|
||||
}
|
||||
_add_one_node(self, one_node_for_adding, node_attr);
|
||||
}
|
||||
return py::none();
|
||||
}
|
||||
|
||||
py::object Graph_add_nodes_from(py::args args, py::kwargs kwargs) {
|
||||
Graph& self = args[0].cast<Graph&>();
|
||||
self.drop_cache();
|
||||
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<py::tuple>();
|
||||
n = n_pair[0];
|
||||
ndict = n_pair[1].cast<py::dict>();
|
||||
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();
|
||||
}
|
||||
_add_one_node(self, n);
|
||||
}
|
||||
node_t id = self.node_to_id[n].cast<node_t>();
|
||||
for (auto item : newdict) {
|
||||
std::string weight_key = weight_to_string(item.first.cast<py::object>());
|
||||
weight_t value = item.second.cast<weight_t>();
|
||||
self.node[id].insert(std::make_pair(weight_key, value));
|
||||
}
|
||||
}
|
||||
return py::none();
|
||||
}
|
||||
|
||||
py::object Graph_remove_node(Graph& self, py::object node_to_remove) {
|
||||
self.drop_cache();
|
||||
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_id = self.node_to_id[node_to_remove].cast<node_t>();
|
||||
for (const auto& neighbor_info : self.adj[node_id]) {
|
||||
node_t neighbor_id = neighbor_info.first;
|
||||
self.adj[neighbor_id].erase(node_id);
|
||||
}
|
||||
self.adj.erase(node_id);
|
||||
self.node.erase(node_id);
|
||||
self.node_to_id.attr("pop")(node_to_remove);
|
||||
self.id_to_node.attr("pop")(node_id);
|
||||
return py::none();
|
||||
}
|
||||
|
||||
py::object Graph_remove_nodes(py::object self, py::list nodes_to_remove) {
|
||||
Graph& self_ = self.cast<Graph&>();
|
||||
self_.drop_cache();
|
||||
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();
|
||||
}
|
||||
|
||||
py::object Graph_number_of_nodes(Graph& self) {
|
||||
return py::cast(int(self.node.size()));
|
||||
}
|
||||
|
||||
py::object Graph_has_node(Graph& self, py::object node) {
|
||||
return py::cast(self.node_to_id.contains(node));
|
||||
}
|
||||
|
||||
py::object Graph_nbunch_iter(py::object self, py::object nbunch) {
|
||||
py::object bunch = py::none();
|
||||
if (nbunch.is_none()) {
|
||||
bunch = self.attr("adj").attr("__iter__")();
|
||||
} else if (self.contains(nbunch)) {
|
||||
py::list nbunch_wrapper = py::list();
|
||||
nbunch_wrapper.append(nbunch);
|
||||
bunch = nbunch_wrapper.attr("__iter__")();
|
||||
} else {
|
||||
py::list nbunch_list = py::list(nbunch), nodes_list = py::list();
|
||||
for (int i = 0; i < py::len(nbunch_list); i++) {
|
||||
py::object n = nbunch_list[i];
|
||||
if (self.contains(n)) {
|
||||
nodes_list.append(n);
|
||||
}
|
||||
}
|
||||
bunch = nbunch_list.attr("__iter__")();
|
||||
}
|
||||
return bunch;
|
||||
}
|
||||
|
||||
void _add_one_edge(Graph& 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 = _add_one_node(self, u_of_edge);
|
||||
} else {
|
||||
u = self.node_to_id[u_of_edge].cast<node_t>();
|
||||
}
|
||||
if (!self.node_to_id.contains(v_of_edge)) {
|
||||
v = _add_one_node(self, v_of_edge);
|
||||
} else {
|
||||
v = self.node_to_id[v_of_edge].cast<node_t>();
|
||||
}
|
||||
py::list items = py::list(edge_attr.attr("items")());
|
||||
self.adj[u][v] = node_attr_dict_factory();
|
||||
self.adj[v][u] = node_attr_dict_factory();
|
||||
for (int i = 0; i < len(items); i++) {
|
||||
py::tuple kv = items[i].cast<py::tuple>();
|
||||
py::object pkey = kv[0];
|
||||
std::string weight_key = weight_to_string(pkey);
|
||||
weight_t value = kv[1].cast<weight_t>();
|
||||
self.adj[u][v].insert(std::make_pair(weight_key, value));
|
||||
self.adj[v][u].insert(std::make_pair(weight_key, value));
|
||||
}
|
||||
}
|
||||
|
||||
py::object Graph_add_edge(py::args args, py::kwargs kwargs) {
|
||||
Graph& self = args[0].cast<Graph&>();
|
||||
self.drop_cache();
|
||||
py::object u_of_edge = args[1], v_of_edge = args[2];
|
||||
py::dict edge_attr = kwargs;
|
||||
_add_one_edge(self, u_of_edge, v_of_edge, edge_attr);
|
||||
return py::none();
|
||||
}
|
||||
|
||||
py::object Graph_add_edges(Graph& self, py::list edges_for_adding, py::list edges_attr) {
|
||||
self.drop_cache();
|
||||
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::tuple>();
|
||||
py::dict edge_attr;
|
||||
if (py::len(edges_attr)) {
|
||||
edge_attr = edges_attr[i].cast<py::dict>();
|
||||
} else {
|
||||
edge_attr = py::dict();
|
||||
}
|
||||
_add_one_edge(self, one_edge_for_adding[0], one_edge_for_adding[1], edge_attr);
|
||||
}
|
||||
return py::none();
|
||||
}
|
||||
|
||||
py::object Graph_add_edges_from(py::args args, py::kwargs attr) {
|
||||
Graph& self = args[0].cast<Graph&>();
|
||||
self.drop_cache();
|
||||
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<py::dict>();
|
||||
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 = _add_one_node(self, u);
|
||||
} else {
|
||||
u_id = self.node_to_id[u].cast<node_t>();
|
||||
}
|
||||
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 = _add_one_node(self, v);
|
||||
} else {
|
||||
v_id = (self.node_to_id[v]).cast<node_t>();
|
||||
}
|
||||
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 i = 0; i < py::len(items); i++) {
|
||||
py::tuple kv = items[i].cast<py::tuple>();
|
||||
py::object pkey = kv[0];
|
||||
std::string weight_key = weight_to_string(pkey);
|
||||
weight_t value = kv[1].cast<weight_t>();
|
||||
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());
|
||||
self.adj[v_id][u_id].insert(datadict.begin(), datadict.end());
|
||||
}
|
||||
return py::none();
|
||||
}
|
||||
|
||||
py::object Graph_add_edges_from_file(Graph& self, py::str file, py::object weighted, py::object is_transform) {
|
||||
self.drop_cache();
|
||||
bool _is_transform = is_transform.cast<bool>();
|
||||
struct commactype : std::ctype<char> {
|
||||
commactype() : std::ctype<char>(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<char>::table_size];
|
||||
std::fill_n(rc, std::ctype<char>::table_size, std::ctype_base::mask());
|
||||
rc[','] = std::ctype_base::space;
|
||||
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::string>();
|
||||
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 = _add_one_node(self, pu);
|
||||
} else {
|
||||
u = self.node_to_id[pu].cast<node_t>();
|
||||
}
|
||||
if (!self.node_to_id.contains(pv)) {
|
||||
v = _add_one_node(self, pv);
|
||||
} else {
|
||||
v = self.node_to_id[pv].cast<node_t>();
|
||||
}
|
||||
if (weighted.cast<bool>()) {
|
||||
in >> weight;
|
||||
self.adj[u][v][key] = self.adj[v][u][key] = weight;
|
||||
} else {
|
||||
if (!self.adj[u].count(v)) {
|
||||
self.adj[u][v] = node_attr_dict_factory();
|
||||
}
|
||||
if (!self.adj[v].count(u)) {
|
||||
self.adj[v][u] = node_attr_dict_factory();
|
||||
}
|
||||
}
|
||||
}
|
||||
in.close();
|
||||
if(_is_transform){
|
||||
Graph_L g_l = graph_to_linkgraph(self, false, key, true, false);
|
||||
self.linkgraph_structure = g_l;
|
||||
self.linkgraph_dirty = false;
|
||||
}
|
||||
return py::none();
|
||||
}
|
||||
|
||||
py::object Graph_add_weighted_edge(Graph& self, py::object u_of_edge, py::object v_of_edge, weight_t weight) {
|
||||
self.drop_cache();
|
||||
py::dict edge_attr;
|
||||
edge_attr["weight"] = weight;
|
||||
_add_one_edge(self, u_of_edge, v_of_edge, edge_attr);
|
||||
return py::none();
|
||||
}
|
||||
|
||||
py::object Graph_remove_edge(Graph& self, py::object u, py::object v) {
|
||||
self.drop_cache();
|
||||
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>();
|
||||
node_t v_id = self.node_to_id[v].cast<node_t>();
|
||||
auto& v_neighbors_info = self.adj[u_id];
|
||||
if (v_neighbors_info.find(v_id) != v_neighbors_info.end()) {
|
||||
v_neighbors_info.erase(v_id);
|
||||
if (u_id != v_id) {
|
||||
self.adj[v_id].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 Graph_remove_edges(py::object self, py::list edges_to_remove) {
|
||||
Graph& self_ = self.cast<Graph&>();
|
||||
for (int i = 0; i < py::len(edges_to_remove); i++) {
|
||||
py::tuple edge = edges_to_remove[i].cast<py::tuple>();
|
||||
py::object u = edge[0], v = edge[1];
|
||||
self.attr("remove_edge")(u, v);
|
||||
}
|
||||
self_.drop_cache();
|
||||
return py::none();
|
||||
}
|
||||
|
||||
py::object Graph_number_of_edges(py::object self, py::object u, py::object v) {
|
||||
if (u.is_none()) {
|
||||
return self.attr("size")();
|
||||
}
|
||||
Graph& self_ = self.cast<Graph&>();
|
||||
node_t u_id = self_.node_to_id.attr("get")(u, -1).cast<node_t>();
|
||||
node_t v_id = self_.node_to_id.attr("get")(v, -1).cast<node_t>();
|
||||
return py::cast(int(self_.adj.count(u_id) && self_.adj[u_id].count(v_id)));
|
||||
}
|
||||
|
||||
py::object Graph_has_edge(Graph& self, py::object u, py::object v) {
|
||||
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>();
|
||||
node_t v_id = self.node_to_id[v].cast<node_t>();
|
||||
auto& v_neighbors_info = self.adj[u_id];
|
||||
if (v_neighbors_info.find(v_id) != v_neighbors_info.end()) {
|
||||
return py::cast(true);
|
||||
}
|
||||
}
|
||||
return py::cast(false);
|
||||
}
|
||||
|
||||
py::object Graph_copy(py::object self) {
|
||||
Graph& self_ = self.cast<Graph&>();
|
||||
py::object G = self.attr("__class__")();
|
||||
Graph& G_ = G.cast<Graph&>();
|
||||
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_.id = self_.id;
|
||||
G_.node = self_.node;
|
||||
G_.adj = self_.adj;
|
||||
return G;
|
||||
}
|
||||
|
||||
py::object Graph_degree(py::object self, py::object weight) {
|
||||
py::dict degree;
|
||||
py::list edges = self.attr("edges").cast<py::list>();
|
||||
py::object u, v;
|
||||
py::dict d;
|
||||
for (int i = 0; i < py::len(edges); i++) {
|
||||
py::tuple edge = edges[i].cast<py::tuple>();
|
||||
u = edge[0];
|
||||
v = edge[1];
|
||||
d = edge[2].cast<py::dict>();
|
||||
if (degree.contains(u)) {
|
||||
degree[u] = py::object(degree[u]) + d.attr("get")(weight, 1);
|
||||
} else {
|
||||
degree[u] = d.attr("get")(weight, 1);
|
||||
}
|
||||
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 Graph_neighbors(py::object self, py::object node) {
|
||||
Graph& self_ = self.cast<Graph&>();
|
||||
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 Graph_generate_linkgraph(py::object self, py::object weight){
|
||||
Graph& G_ = self.cast<Graph&>();
|
||||
std::string w = weight_to_string(weight);
|
||||
Graph_L g_l = graph_to_linkgraph(G_, false, w, true, false);
|
||||
G_.linkgraph_dirty = false;
|
||||
G_.linkgraph_structure = g_l;
|
||||
return py::none();
|
||||
}
|
||||
|
||||
py::object Graph_nodes_subgraph(py::object self, py::list from_nodes) {
|
||||
py::object G = self.attr("__class__")();
|
||||
Graph& self_ = self.cast<Graph&>();
|
||||
Graph& G_ = G.cast<Graph&>();
|
||||
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];
|
||||
_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::tuple>();
|
||||
py::object v = item[0];
|
||||
py::object edge_attr = item[1];
|
||||
if (from_nodes.contains(v)) {
|
||||
_add_one_edge(G_, node, v, edge_attr);
|
||||
}
|
||||
}
|
||||
}
|
||||
return G;
|
||||
}
|
||||
|
||||
py::object Graph_ego_subgraph(py::object self, py::object center) {
|
||||
py::list neighbors_of_center = py::list(self.attr("all_neighbors")(center));
|
||||
neighbors_of_center.append(center);
|
||||
return self.attr("nodes_subgraph")(neighbors_of_center);
|
||||
}
|
||||
|
||||
py::object Graph_size(py::object self, py::object weight) {
|
||||
py::dict degree = self.attr("degree")(weight).cast<py::dict>();
|
||||
weight_t s = 0;
|
||||
for (auto item : degree) {
|
||||
s += item.second.cast<weight_t>();
|
||||
}
|
||||
return (weight.is_none()) ? py::cast(int(s) / 2) : py::cast(s / 2);
|
||||
}
|
||||
|
||||
py::object Graph_is_directed(py::object self) {
|
||||
return py::cast(false);
|
||||
}
|
||||
|
||||
py::object Graph_is_multigraph(py::object self) {
|
||||
return py::cast(false);
|
||||
}
|
||||
|
||||
py::object Graph_to_index_node_graph(py::object self, py::object begin_index) {
|
||||
py::object G = self.attr("__class__")();
|
||||
G.attr("graph").attr("update")(self.attr("graph"));
|
||||
py::dict index_of_node = py::dict(), node_of_index = py::dict();
|
||||
int begin = begin_index.cast<int>();
|
||||
int index = 0;
|
||||
for (auto item : self.attr("nodes").cast<py::dict>()) {
|
||||
py::object node = item.first.cast<py::object>();
|
||||
py::dict node_attr = item.second.cast<py::dict>();
|
||||
G.attr("add_node")(py::cast(index + begin), **node_attr);
|
||||
index_of_node[node] = index + begin;
|
||||
node_of_index[py::cast(index + begin)] = node;
|
||||
index++;
|
||||
}
|
||||
for (auto item : self.attr("adj").cast<py::dict>()) {
|
||||
py::object u = item.first.cast<py::object>();
|
||||
py::dict nbrs = item.second.cast<py::dict>();
|
||||
for (auto item_ : nbrs) {
|
||||
py::object v = item_.first.cast<py::object>();
|
||||
py::dict edge_data = item_.second.cast<py::dict>();
|
||||
G.attr("add_edge")(index_of_node[u], index_of_node[v], **edge_data);
|
||||
}
|
||||
}
|
||||
return py::make_tuple(G, index_of_node, node_of_index);
|
||||
}
|
||||
|
||||
py::object Graph_py(py::object self) {
|
||||
py::object G = py::module_::import("easygraph").attr("Graph")();
|
||||
G.attr("graph").attr("update")(self.attr("graph"));
|
||||
G.attr("adj").attr("update")(self.attr("adj"));
|
||||
G.attr("nodes").attr("update")(self.attr("nodes"));
|
||||
return G;
|
||||
}
|
||||
|
||||
py::object Graph::get_nodes() {
|
||||
if (this->dirty_nodes) {
|
||||
py::dict nodes = py::dict();
|
||||
for (const auto& node_info : node) {
|
||||
node_t id = node_info.first;
|
||||
const auto& node_attr = node_info.second;
|
||||
nodes[this->id_to_node[py::cast(id)]] = attr_to_dict(node_attr);
|
||||
}
|
||||
this->nodes_cache = nodes;
|
||||
this->dirty_nodes = false;
|
||||
}
|
||||
return this->nodes_cache;
|
||||
}
|
||||
|
||||
py::object Graph::get_name() {
|
||||
return this->graph.attr("get")("name", "");
|
||||
}
|
||||
|
||||
py::object Graph::set_name(py::object name) {
|
||||
this->graph[py::cast("name")] = name;
|
||||
return py::none();
|
||||
}
|
||||
|
||||
py::object Graph::get_node_index() {
|
||||
py::dict node_index = py::dict();
|
||||
int len = py::len(this->node_to_id);
|
||||
for(int i = 1; i <= len; i++){
|
||||
node_index[this->id_to_node[py::cast(i)]] = py::cast(i - 1);
|
||||
}
|
||||
return node_index;
|
||||
}
|
||||
py::object Graph::get_graph() {
|
||||
return this->graph;
|
||||
}
|
||||
|
||||
py::object Graph::get_adj() {
|
||||
if (this->dirty_adj) {
|
||||
py::dict adj = py::dict();
|
||||
for (const auto& ego_edges : this->adj) {
|
||||
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);
|
||||
}
|
||||
adj[this->id_to_node[py::cast(start_point)]] = ego_edges_dict;
|
||||
}
|
||||
this->adj_cache = adj;
|
||||
this->dirty_adj = false;
|
||||
}
|
||||
return this->adj_cache;
|
||||
}
|
||||
|
||||
py::object Graph::get_edges() {
|
||||
py::list edges = py::list();
|
||||
std::set<std::pair<node_t, node_t> > 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));
|
||||
seen.insert(std::make_pair(v, u));
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
Graph_L Graph::_get_linkgraph_structure() {
|
||||
return this->linkgraph_structure;
|
||||
}
|
||||
bool Graph::is_linkgraph_dirty(){
|
||||
return this->linkgraph_dirty;
|
||||
}
|
||||
std::vector<graph_edge> Graph::_get_edges(bool if_directed) {
|
||||
std::vector<graph_edge> edges;
|
||||
std::set<std::pair<node_t, node_t> > 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));
|
||||
if(!if_directed){
|
||||
seen.insert(std::make_pair(v, u));
|
||||
}
|
||||
|
||||
edges.emplace_back(u, v, edge_attr);
|
||||
}
|
||||
}
|
||||
}
|
||||
return edges;
|
||||
}
|
||||
|
||||
void Graph::drop_cache() {
|
||||
dirty_nodes = true;
|
||||
dirty_adj = true;
|
||||
linkgraph_dirty = true;
|
||||
csr_graph = nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<CSRGraph> Graph::gen_CSR(const std::string& weight) {
|
||||
if (csr_graph != nullptr) {
|
||||
if (csr_graph->W_map.find(weight) == csr_graph->W_map.end()) {
|
||||
auto W = std::make_shared<std::vector<double>>();
|
||||
|
||||
// According to C++ Standard, the iteration order of a unordered contrainer will
|
||||
// not change without rehashing which only happens during a insert.
|
||||
for (node_t n : csr_graph->nodes) {
|
||||
// if n is not in adj, this way can raise an exception
|
||||
const auto& n_adjs = adj.find(n)->second;
|
||||
|
||||
for (auto adj_it = n_adjs.begin(); adj_it != n_adjs.end(); ++adj_it) {
|
||||
const edge_attr_dict_factory& edge_attr = adj_it->second;
|
||||
auto edge_it = edge_attr.find(weight);
|
||||
weight_t w = edge_it != edge_attr.end() ? edge_it->second : 1.0;
|
||||
|
||||
W->push_back(w);
|
||||
}
|
||||
}
|
||||
|
||||
csr_graph->W_map[weight] = W;
|
||||
}
|
||||
} else {
|
||||
// the graph has been modified
|
||||
|
||||
csr_graph = std::make_shared<CSRGraph>();
|
||||
|
||||
std::vector<node_t>& nodes = csr_graph->nodes;
|
||||
for (auto it = node.begin(); it != node.end(); ++it) {
|
||||
nodes.push_back(it->first);
|
||||
}
|
||||
|
||||
std::sort(nodes.begin(), nodes.end());
|
||||
|
||||
std::unordered_map<node_t, int>& node2idx = csr_graph->node2idx;
|
||||
|
||||
for (int i = 0; i < nodes.size(); ++i) {
|
||||
node2idx[nodes[i]] = i;
|
||||
}
|
||||
|
||||
std::vector<int>& V = csr_graph->V;
|
||||
std::vector<int>& E = csr_graph->E;
|
||||
auto W = std::make_shared<std::vector<double>>();
|
||||
|
||||
for (int idx = 0; idx < nodes.size(); ++idx) {
|
||||
V.push_back(E.size());
|
||||
|
||||
node_t n = nodes[idx];
|
||||
|
||||
// if n is not in adj, this way can raise an exception
|
||||
const auto& n_adjs = adj.find(n)->second;
|
||||
|
||||
for (auto adj_it = n_adjs.begin(); adj_it != n_adjs.end(); ++adj_it) {
|
||||
const edge_attr_dict_factory& edge_attr = adj_it->second;
|
||||
auto edge_it = edge_attr.find(weight);
|
||||
weight_t w = edge_it != edge_attr.end() ? edge_it->second : 1.0;
|
||||
|
||||
W->push_back(w);
|
||||
E.push_back(node2idx[adj_it->first]);
|
||||
}
|
||||
}
|
||||
|
||||
V.push_back(E.size());
|
||||
|
||||
csr_graph->W_map[weight] = W;
|
||||
}
|
||||
|
||||
return csr_graph;
|
||||
}
|
||||
|
||||
std::shared_ptr<CSRGraph> Graph::gen_CSR() {
|
||||
if (csr_graph != nullptr) {
|
||||
if (csr_graph->unweighted_W.size() != csr_graph->E.size()) {
|
||||
csr_graph->unweighted_W = std::vector<double>(csr_graph->E.size(), 1.0);
|
||||
}
|
||||
} else {
|
||||
// the graph has been modified
|
||||
|
||||
csr_graph = std::make_shared<CSRGraph>();
|
||||
|
||||
std::vector<node_t>& nodes = csr_graph->nodes;
|
||||
for (auto it = node.begin(); it != node.end(); ++it) {
|
||||
nodes.push_back(it->first);
|
||||
}
|
||||
|
||||
std::sort(nodes.begin(), nodes.end());
|
||||
|
||||
std::unordered_map<node_t, int>& node2idx = csr_graph->node2idx;
|
||||
|
||||
for (int i = 0; i < nodes.size(); ++i) {
|
||||
node2idx[nodes[i]] = i;
|
||||
}
|
||||
|
||||
std::vector<int>& V = csr_graph->V;
|
||||
std::vector<int>& E = csr_graph->E;
|
||||
|
||||
for (int idx = 0; idx < nodes.size(); ++idx) {
|
||||
V.push_back(E.size());
|
||||
|
||||
node_t n = nodes[idx];
|
||||
|
||||
// if n is not in adj, this way can raise an exception
|
||||
const auto& n_adjs = adj.find(n)->second;
|
||||
|
||||
for (auto adj_it = n_adjs.begin(); adj_it != n_adjs.end(); ++adj_it) {
|
||||
const edge_attr_dict_factory& edge_attr = adj_it->second;
|
||||
|
||||
E.push_back(node2idx[adj_it->first]);
|
||||
}
|
||||
}
|
||||
|
||||
V.push_back(E.size());
|
||||
csr_graph->unweighted_W = std::vector<double>(E.size(), 1.0);
|
||||
}
|
||||
|
||||
return csr_graph;
|
||||
}
|
||||
|
||||
std::shared_ptr<std::vector<int>> Graph::gen_CSR_sources(const py::object& py_sources) {
|
||||
auto sources = std::make_shared<std::vector<int>>();
|
||||
|
||||
if (py_sources.is_none()) {
|
||||
for (int i = 0; i < csr_graph->V.size() - 1; ++i) {
|
||||
sources->push_back(i);
|
||||
}
|
||||
} else {
|
||||
for (auto it = py_sources.begin(); it != py_sources.end(); ++it) {
|
||||
sources->push_back(csr_graph->node2idx[node_to_id[*it].cast<node_t>()]);
|
||||
}
|
||||
}
|
||||
|
||||
return sources;
|
||||
}
|
||||
|
||||
std::shared_ptr<COOGraph> Graph::gen_COO() {
|
||||
if (coo_graph != nullptr) {
|
||||
if (coo_graph->unweighted_W.size() != coo_graph->row.size()) {
|
||||
coo_graph->unweighted_W = std::vector<double>(coo_graph->row.size(), 1.0);
|
||||
}
|
||||
} else {
|
||||
|
||||
coo_graph = std::make_shared<COOGraph>();
|
||||
|
||||
std::vector<node_t>& nodes = coo_graph->nodes;
|
||||
for (auto it = node.begin(); it != node.end(); ++it) {
|
||||
nodes.push_back(it->first);
|
||||
}
|
||||
|
||||
std::sort(nodes.begin(), nodes.end());
|
||||
|
||||
std::unordered_map<node_t, int>& node2idx = coo_graph->node2idx;
|
||||
for (int i = 0; i < nodes.size(); ++i) {
|
||||
node2idx[nodes[i]] = i;
|
||||
}
|
||||
|
||||
std::vector<int>& row = coo_graph->row;
|
||||
std::vector<int>& col = coo_graph->col;
|
||||
std::vector<double>& unweighted_W = coo_graph->unweighted_W;
|
||||
|
||||
for (int idx = 0; idx < nodes.size(); ++idx) {
|
||||
node_t n = nodes[idx];
|
||||
|
||||
const auto& n_adjs = adj.find(n)->second;
|
||||
|
||||
for (auto adj_it = n_adjs.begin(); adj_it != n_adjs.end(); ++adj_it) {
|
||||
node_t neighbor = adj_it->first;
|
||||
|
||||
row.push_back(idx);
|
||||
col.push_back(node2idx[neighbor]);
|
||||
|
||||
unweighted_W.push_back(1.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return coo_graph;
|
||||
}
|
||||
|
||||
std::shared_ptr<COOGraph> Graph::gen_COO(const std::string& weight) {
|
||||
if (coo_graph != nullptr) {
|
||||
if (coo_graph->W_map.find(weight) == coo_graph->W_map.end()) {
|
||||
auto W = std::make_shared<std::vector<double>>();
|
||||
|
||||
for (node_t n : coo_graph->nodes) {
|
||||
const auto& n_adjs = adj.find(n)->second;
|
||||
|
||||
for (auto adj_it = n_adjs.begin(); adj_it != n_adjs.end(); ++adj_it) {
|
||||
const edge_attr_dict_factory& edge_attr = adj_it->second;
|
||||
auto edge_it = edge_attr.find(weight);
|
||||
weight_t w = edge_it != edge_attr.end() ? edge_it->second : 1.0;
|
||||
|
||||
W->push_back(w);
|
||||
}
|
||||
}
|
||||
|
||||
coo_graph->W_map[weight] = W;
|
||||
}
|
||||
} else {
|
||||
coo_graph = std::make_shared<COOGraph>();
|
||||
|
||||
std::vector<node_t>& nodes = coo_graph->nodes;
|
||||
for (auto it = node.begin(); it != node.end(); ++it) {
|
||||
nodes.push_back(it->first);
|
||||
}
|
||||
|
||||
std::sort(nodes.begin(), nodes.end());
|
||||
|
||||
std::unordered_map<node_t, int>& node2idx = coo_graph->node2idx;
|
||||
for (int i = 0; i < nodes.size(); ++i) {
|
||||
node2idx[nodes[i]] = i;
|
||||
}
|
||||
|
||||
std::vector<int>& row = coo_graph->row;
|
||||
std::vector<int>& col = coo_graph->col;
|
||||
auto W = std::make_shared<std::vector<double>>();
|
||||
|
||||
for (int idx = 0; idx < nodes.size(); ++idx) {
|
||||
node_t n = nodes[idx];
|
||||
|
||||
const auto& n_adjs = adj.find(n)->second;
|
||||
|
||||
for (auto adj_it = n_adjs.begin(); adj_it != n_adjs.end(); ++adj_it) {
|
||||
const edge_attr_dict_factory& edge_attr = adj_it->second;
|
||||
auto edge_it = edge_attr.find(weight);
|
||||
weight_t w = edge_it != edge_attr.end() ? edge_it->second : 1.0;
|
||||
|
||||
row.push_back(idx);
|
||||
col.push_back(node2idx[adj_it->first]);
|
||||
|
||||
W->push_back(w);
|
||||
}
|
||||
}
|
||||
|
||||
coo_graph->W_map[weight] = W;
|
||||
}
|
||||
|
||||
return coo_graph;
|
||||
}
|
||||
|
||||
std::shared_ptr<COOGraph> Graph::transfer_csr_to_coo(const std::shared_ptr<CSRGraph>& csr_graph) {
|
||||
auto coo_graph = std::make_shared<COOGraph>();
|
||||
|
||||
coo_graph->nodes = csr_graph->nodes;
|
||||
coo_graph->node2idx = csr_graph->node2idx;
|
||||
|
||||
const std::vector<int>& V = csr_graph->V;
|
||||
const std::vector<int>& E = csr_graph->E;
|
||||
int num_edges = E.size();
|
||||
|
||||
coo_graph->row.reserve(num_edges);
|
||||
coo_graph->col.reserve(num_edges);
|
||||
|
||||
std::vector<int>& row = coo_graph->row;
|
||||
std::vector<int>& col = coo_graph->col;
|
||||
|
||||
for (int i = 0; i < V.size() - 1; ++i) {
|
||||
int start_idx = V[i];
|
||||
int end_idx = V[i + 1];
|
||||
|
||||
for (int j = start_idx; j < end_idx; ++j) {
|
||||
row.push_back(i);
|
||||
col.push_back(E[j]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!csr_graph->unweighted_W.empty()) {
|
||||
coo_graph->unweighted_W = csr_graph->unweighted_W;
|
||||
} else {
|
||||
coo_graph->W_map = csr_graph->W_map;
|
||||
}
|
||||
|
||||
return coo_graph;
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
#pragma once
|
||||
|
||||
#include "../common/common.h"
|
||||
#include "csr_graph.h"
|
||||
#include "linkgraph.h"
|
||||
#include "coo_graph.h"
|
||||
|
||||
// old version
|
||||
struct Graph {
|
||||
node_dict_factory node;
|
||||
adj_dict_factory adj;
|
||||
Graph_L linkgraph_structure;
|
||||
std::shared_ptr<CSRGraph> csr_graph;
|
||||
py::kwargs node_to_id, id_to_node, graph;
|
||||
node_t id;
|
||||
bool dirty_nodes, dirty_adj, linkgraph_dirty;
|
||||
py::object nodes_cache, adj_cache;
|
||||
std::shared_ptr<COOGraph> coo_graph;
|
||||
|
||||
Graph();
|
||||
py::object get_nodes();
|
||||
py::object get_name();
|
||||
py::object set_name(py::object name);
|
||||
py::object get_graph();
|
||||
py::object get_adj();
|
||||
py::object get_edges();
|
||||
py::object get_node_index();
|
||||
std::vector<graph_edge> _get_edges(bool if_directed=true);
|
||||
bool is_linkgraph_dirty();
|
||||
Graph_L _get_linkgraph_structure();
|
||||
void drop_cache();
|
||||
|
||||
std::shared_ptr<CSRGraph> gen_CSR(const std::string& weight);
|
||||
std::shared_ptr<CSRGraph> gen_CSR();
|
||||
std::shared_ptr<std::vector<int>> gen_CSR_sources(const py::object& py_sources);
|
||||
std::shared_ptr<COOGraph> gen_COO();
|
||||
std::shared_ptr<COOGraph> gen_COO(const std::string& weight);
|
||||
std::shared_ptr<COOGraph> transfer_csr_to_coo(const std::shared_ptr<CSRGraph>& csr_graph);
|
||||
};
|
||||
|
||||
py::object Graph__init__(py::args args, py::kwargs kwargs);
|
||||
py::object Graph__iter__(py::object self);
|
||||
py::object Graph__len__(py::object self);
|
||||
py::object Graph__contains__(py::object self, py::object node);
|
||||
py::object Graph__getitem__(py::object self, py::object node);
|
||||
py::object Graph_add_node(py::args args, py::kwargs kwargs);
|
||||
py::object Graph_add_nodes(Graph& self, py::list nodes_for_adding, py::list nodes_attr);
|
||||
py::object Graph_add_nodes_from(py::args args, py::kwargs kwargs);
|
||||
py::object Graph_remove_node(Graph& self, py::object node_to_remove);
|
||||
py::object Graph_remove_nodes(py::object self, py::list nodes_to_remove);
|
||||
py::object Graph_number_of_nodes(Graph& self);
|
||||
py::object Graph_has_node(Graph& self, py::object node);
|
||||
py::object Graph_nbunch_iter(py::object self, py::object nbunch);
|
||||
py::object Graph_add_edge(py::args args, py::kwargs kwargs);
|
||||
py::object Graph_add_edges(Graph& self, py::list edges_for_adding, py::list edges_attr);
|
||||
py::object Graph_add_edges_from(py::args args, py::kwargs attr);
|
||||
py::object Graph_add_edges_from_file(Graph& self, py::str file, py::object weighted, py::object is_transform);
|
||||
py::object Graph_add_weighted_edge(Graph& self, py::object u_of_edge, py::object v_of_edge, weight_t weight);
|
||||
py::object Graph_remove_edge(Graph& self, py::object u, py::object v);
|
||||
py::object Graph_remove_edges(py::object self, py::list edges_to_remove);
|
||||
py::object Graph_number_of_edges(py::object self, py::object u, py::object v);
|
||||
py::object Graph_has_edge(Graph& self, py::object u, py::object v);
|
||||
py::object Graph_copy(py::object self);
|
||||
py::object Graph_degree(py::object self, py::object weight);
|
||||
py::object Graph_neighbors(py::object self, py::object node);
|
||||
py::object Graph_nodes_subgraph(py::object self, py::list from_nodes);
|
||||
py::object Graph_ego_subgraph(py::object self, py::object center);
|
||||
py::object Graph_size(py::object self, py::object weight);
|
||||
py::object Graph_is_directed(py::object self);
|
||||
py::object Graph_is_multigraph(py::object self);
|
||||
py::object Graph_to_index_node_graph(py::object self, py::object begin_index);
|
||||
py::object Graph_generate_linkgraph(py::object self, py::object weight);
|
||||
py::object Graph_py(py::object self);
|
||||
@@ -0,0 +1,91 @@
|
||||
#pragma once
|
||||
#include "../common/common.h"
|
||||
|
||||
struct LinkEdge{
|
||||
// 终点
|
||||
node_t to;
|
||||
// 边权重
|
||||
weight_t w;
|
||||
// 同起点的上一条边的编号
|
||||
node_t next;
|
||||
|
||||
};
|
||||
struct Graph_L{
|
||||
int n;
|
||||
int e;
|
||||
bool is_directed;
|
||||
bool is_deg;
|
||||
std::vector<int> head;
|
||||
std::vector<LinkEdge> edges;
|
||||
std::vector<int> degree;
|
||||
int max_deg = -1;
|
||||
|
||||
|
||||
Graph_L(int vertex_num = 0, bool directed = true, bool deg = false){
|
||||
this->n = vertex_num;
|
||||
this->e = 0;
|
||||
this->is_deg = deg;
|
||||
this->is_directed = directed;
|
||||
LinkEdge le;
|
||||
le.to = -1;
|
||||
le.next = -1;
|
||||
edges.emplace_back(le);
|
||||
if(n > 0){
|
||||
head.resize(vertex_num + 1);
|
||||
if(deg){
|
||||
degree.resize(vertex_num + 1);
|
||||
for(int i = 0; i < vertex_num+1; i++){
|
||||
head[i] = -1;
|
||||
degree[i] = 0;
|
||||
}
|
||||
}
|
||||
else{
|
||||
for(int i = 0; i < vertex_num+1; i++){
|
||||
head[i] = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void add_unweighted_edge(const int &u, const int &v) {
|
||||
LinkEdge le;
|
||||
le.to = v;
|
||||
le.next = head[u];
|
||||
this->edges.emplace_back(le);
|
||||
this->head[u] = this->e;
|
||||
this->e += 1;
|
||||
if(this->is_deg){
|
||||
this->degree[u]++;
|
||||
this->max_deg = std::max(this->max_deg, this->degree[u]);
|
||||
}
|
||||
}
|
||||
|
||||
void add_weighted_edge(const int &u, const int &v, const double &w) {
|
||||
// printf("u:%d,v:%d w:%.2f\n",u,v,w);
|
||||
LinkEdge le;
|
||||
this->e += 1;
|
||||
le.to = v;
|
||||
le.w = w;
|
||||
le.next = head[u];
|
||||
// printf("next:%d\n",this->head[u]);
|
||||
this->edges.emplace_back(le);
|
||||
this->head[u] = this->e;
|
||||
// printf("head:%d\n",this->head[u]);
|
||||
|
||||
if(this->is_deg){
|
||||
this->degree[u]++;
|
||||
this->max_deg = std::max(this->max_deg, this->degree[u]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct compare_node {
|
||||
compare_node(){}
|
||||
compare_node(int _x, int _d) {x = _x; d = _d;}
|
||||
int x, d;
|
||||
bool operator < (const compare_node &rhs) const {
|
||||
return d > rhs.d;
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<double> _dijkstra(const Graph_L& G_l, int source, int target);
|
||||
@@ -0,0 +1,16 @@
|
||||
#include "operation.h"
|
||||
#include "graph.h"
|
||||
|
||||
py::object density(py::object G) {
|
||||
Graph& G_ = G.cast<Graph&>();
|
||||
int n = G_.node.size();
|
||||
int m = G.attr("number_of_edges")().cast<int>();
|
||||
if (m == 0 || n <= 1) {
|
||||
return py::cast(0);
|
||||
}
|
||||
weight_t d = m * 1.0 / (n * (n - 1));
|
||||
if(G.attr("is_directed")().equal(py::cast(false))){
|
||||
d*=2;
|
||||
}
|
||||
return py::cast(d);
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "../common/common.h"
|
||||
|
||||
py::object density(py::object G);
|
||||
@@ -0,0 +1,42 @@
|
||||
#include "../common/common.h"
|
||||
|
||||
class Segment_tree_zkw {
|
||||
private:
|
||||
int tn;
|
||||
int size;
|
||||
public:
|
||||
std::vector<int> t;
|
||||
std::vector<int> num;
|
||||
Segment_tree_zkw(int N){
|
||||
size = (N+1)<<2;
|
||||
this->t = std::vector<int>(size+1 , INT_MAX);
|
||||
this->num = std::vector<int>(size+1 , 0);
|
||||
}
|
||||
void init(int N) {
|
||||
for(int i = 0; i < size; i++){
|
||||
this->t[i] = INT_MAX;
|
||||
this->num[i] = 0;
|
||||
}
|
||||
|
||||
tn = 1;
|
||||
while(tn < N) tn <<= 1;
|
||||
--tn;
|
||||
for (int i = 1; i <= N; ++i)
|
||||
this->num[i + tn] = i;
|
||||
}
|
||||
|
||||
void change(int p, const int &k) {
|
||||
p += tn; this->t[p] = k; p >>= 1;
|
||||
while (p) {
|
||||
if (this->t[p<<1] < this->t[p<<1|1]) {
|
||||
this->t[p] = this->t[p<<1];
|
||||
this->num[p] = this->num[p<<1];
|
||||
}
|
||||
else {
|
||||
this->t[p] = this->t[p<<1|1];
|
||||
this->num[p] = this->num[p<<1|1];
|
||||
}
|
||||
p >>= 1;
|
||||
}
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user