chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
#include "path.h"
|
||||
#include "mst.h"
|
||||
@@ -0,0 +1,148 @@
|
||||
#include "path.h"
|
||||
|
||||
#ifdef _OPENMP
|
||||
#include <omp.h>
|
||||
#endif
|
||||
|
||||
#include "../../classes/graph.h"
|
||||
#include "../../common/utils.h"
|
||||
#include "../../classes/linkgraph.h"
|
||||
|
||||
#include <vector>
|
||||
#include <queue>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
|
||||
double _bfs_sum(const Graph_L& G_l, int source) {
|
||||
int N = G_l.n;
|
||||
std::vector<int> dis(N + 1, -1);
|
||||
std::queue<int> q;
|
||||
|
||||
dis[source] = 0;
|
||||
q.push(source);
|
||||
|
||||
double sum = 0.0;
|
||||
int visited_count = 0;
|
||||
|
||||
const std::vector<int>& head = G_l.head;
|
||||
const std::vector<LinkEdge>& E = G_l.edges;
|
||||
|
||||
while (!q.empty()) {
|
||||
int u = q.front();
|
||||
q.pop();
|
||||
sum += dis[u];
|
||||
visited_count++;
|
||||
|
||||
for (int p = head[u]; p != -1; p = E[p].next) {
|
||||
int v = E[p].to;
|
||||
if (dis[v] == -1) {
|
||||
dis[v] = dis[u] + 1;
|
||||
q.push(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
return (visited_count == N) ? sum : -1.0;
|
||||
}
|
||||
|
||||
double _dijkstra_sum(const Graph_L& G_l, int source) {
|
||||
int N = G_l.n;
|
||||
const double INF = std::numeric_limits<double>::infinity();
|
||||
std::vector<double> dis(N + 1, INF);
|
||||
std::priority_queue<std::pair<double, int>,
|
||||
std::vector<std::pair<double, int>>,
|
||||
std::greater<std::pair<double, int>>> pq;
|
||||
|
||||
dis[source] = 0.0;
|
||||
pq.push({0.0, source});
|
||||
|
||||
double sum = 0.0;
|
||||
int visited_count = 0;
|
||||
|
||||
const std::vector<int>& head = G_l.head;
|
||||
const std::vector<LinkEdge>& E = G_l.edges;
|
||||
|
||||
while (!pq.empty()) {
|
||||
auto top_node = pq.top();
|
||||
double d = top_node.first;
|
||||
int u = top_node.second;
|
||||
pq.pop();
|
||||
|
||||
if (d > dis[u]) continue;
|
||||
sum += d;
|
||||
visited_count++;
|
||||
|
||||
for (int p = head[u]; p != -1; p = E[p].next) {
|
||||
int v = E[p].to;
|
||||
double w = static_cast<double>(E[p].w);
|
||||
if (dis[u] + w < dis[v]) {
|
||||
dis[v] = dis[u] + w;
|
||||
pq.push({dis[v], v});
|
||||
}
|
||||
}
|
||||
}
|
||||
return (visited_count == N) ? sum : -1.0;
|
||||
}
|
||||
|
||||
|
||||
py::object average_shortest_path_length(py::object G, py::object weight, py::object method) {
|
||||
Graph& G_ = G.cast<Graph&>();
|
||||
bool is_directed = G.attr("is_directed")().cast<bool>();
|
||||
|
||||
std::string weight_key = "";
|
||||
if (!weight.is_none()) {
|
||||
weight_key = weight.cast<std::string>();
|
||||
}
|
||||
|
||||
std::string method_str;
|
||||
if (method.is_none()) {
|
||||
method_str = weight.is_none() ? "single_source_bfs" : "dijkstra";
|
||||
} else {
|
||||
method_str = method.cast<std::string>();
|
||||
}
|
||||
|
||||
if(G_.linkgraph_dirty){
|
||||
G_.linkgraph_structure = graph_to_linkgraph(G_, is_directed, weight_key, true, false);
|
||||
G_.linkgraph_dirty = false;
|
||||
}
|
||||
const Graph_L& G_l = G_.linkgraph_structure;
|
||||
|
||||
int N = G_l.n;
|
||||
if (N <= 1) return py::float_(0.0);
|
||||
|
||||
double total_sum = 0.0;
|
||||
bool is_connected = true;
|
||||
|
||||
{
|
||||
py::gil_scoped_release release;
|
||||
|
||||
#pragma omp parallel for reduction(+:total_sum) schedule(dynamic)
|
||||
for (int i = 1; i <= N; i++) {
|
||||
if (!is_connected) continue;
|
||||
|
||||
double local_sum = 0.0;
|
||||
if (method_str == "single_source_bfs" || method_str == "unweighted") {
|
||||
local_sum = _bfs_sum(G_l, i);
|
||||
} else {
|
||||
local_sum = _dijkstra_sum(G_l, i);
|
||||
}
|
||||
|
||||
if (local_sum < 0) {
|
||||
#pragma omp critical
|
||||
is_connected = false;
|
||||
} else {
|
||||
total_sum += local_sum;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_connected) {
|
||||
if (is_directed) {
|
||||
throw py::value_error("Graph is not strongly connected.");
|
||||
}
|
||||
else {
|
||||
throw py::value_error("Graph is not connected.");
|
||||
}
|
||||
}
|
||||
|
||||
return py::float_(total_sum / (static_cast<double>(N) * (N - 1)));
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
#include "path.h"
|
||||
|
||||
#ifdef _OPENMP
|
||||
#include <omp.h>
|
||||
#endif
|
||||
|
||||
#include "../../classes/graph.h"
|
||||
#include "../../common/utils.h"
|
||||
#include "../../classes/linkgraph.h"
|
||||
|
||||
#include <vector>
|
||||
#include <queue>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <pybind11/pybind11.h>
|
||||
|
||||
namespace py = pybind11;
|
||||
|
||||
double _bfs_eccentricity(const Graph_L& G_l, int source) {
|
||||
int N = G_l.n;
|
||||
std::vector<int> dis(N + 1, -1);
|
||||
std::queue<int> q;
|
||||
|
||||
dis[source] = 0;
|
||||
q.push(source);
|
||||
|
||||
double max_dist = 0.0;
|
||||
int visited_count = 0;
|
||||
|
||||
const std::vector<int>& head = G_l.head;
|
||||
const std::vector<LinkEdge>& E = G_l.edges;
|
||||
|
||||
while (!q.empty()) {
|
||||
int u = q.front();
|
||||
q.pop();
|
||||
visited_count++;
|
||||
|
||||
if (dis[u] > max_dist) {
|
||||
max_dist = dis[u];
|
||||
}
|
||||
|
||||
for (int p = head[u]; p != -1; p = E[p].next) {
|
||||
int v = E[p].to;
|
||||
if (dis[v] == -1) {
|
||||
dis[v] = dis[u] + 1;
|
||||
q.push(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (visited_count == N) ? max_dist : -1.0;
|
||||
}
|
||||
|
||||
|
||||
py::object eccentricity(py::object G, py::object v = py::none(), py::object sp = py::none()) {
|
||||
Graph& G_ = G.cast<Graph&>();
|
||||
|
||||
bool is_directed = py::hasattr(G, "is_directed") ? G.attr("is_directed")().cast<bool>() : false;
|
||||
|
||||
int order = py::len(G.attr("nodes"));
|
||||
|
||||
if (!sp.is_none()) {
|
||||
py::dict result;
|
||||
py::dict sp_dict = sp.cast<py::dict>();
|
||||
|
||||
py::list nodes_to_check;
|
||||
bool is_single = false;
|
||||
|
||||
if (v.is_none()) {
|
||||
nodes_to_check = py::list(G.attr("nodes"));
|
||||
} else if (py::isinstance<py::list>(v) || py::isinstance<py::tuple>(v) || py::isinstance<py::set>(v)) {
|
||||
nodes_to_check = py::list(v);
|
||||
} else {
|
||||
nodes_to_check.append(v);
|
||||
is_single = true;
|
||||
}
|
||||
|
||||
for (py::handle n : nodes_to_check) {
|
||||
if (!sp_dict.contains(n)) {
|
||||
throw py::type_error("Format of 'sp' is invalid.");
|
||||
}
|
||||
py::dict length_dict = sp_dict[n].cast<py::dict>();
|
||||
if (py::len(length_dict) != order) {
|
||||
std::string msg = is_directed ?
|
||||
"Found infinite path length because the digraph is not strongly connected" :
|
||||
"Found infinite path length because the graph is not connected";
|
||||
throw py::value_error(msg);
|
||||
}
|
||||
|
||||
double max_val = 0.0;
|
||||
for (auto item : length_dict) {
|
||||
double val = item.second.cast<double>();
|
||||
if (val > max_val) max_val = val;
|
||||
}
|
||||
result[n] = max_val;
|
||||
}
|
||||
|
||||
if (is_single) return result[v];
|
||||
return result;
|
||||
}
|
||||
|
||||
if (G_.linkgraph_dirty) {
|
||||
G_.linkgraph_structure = graph_to_linkgraph(G_, is_directed, "", true, false);
|
||||
G_.linkgraph_dirty = false;
|
||||
}
|
||||
const Graph_L& G_l = G_.linkgraph_structure;
|
||||
|
||||
int N = G_l.n;
|
||||
if (N == 0) return py::dict();
|
||||
|
||||
py::dict node_index;
|
||||
std::vector<py::object> index_to_node_vec;
|
||||
|
||||
if (py::hasattr(G, "node_index")) {
|
||||
node_index = G.attr("node_index").cast<py::dict>();
|
||||
} else {
|
||||
int idx = 0;
|
||||
for (py::handle n : G.attr("nodes")) {
|
||||
node_index[n] = py::cast(idx++);
|
||||
}
|
||||
}
|
||||
|
||||
if (py::hasattr(G, "index_node")) {
|
||||
py::object idx_n = G.attr("index_node");
|
||||
if (py::isinstance<py::list>(idx_n)) {
|
||||
py::list l = idx_n.cast<py::list>();
|
||||
for (auto item : l) index_to_node_vec.push_back(item.cast<py::object>());
|
||||
} else if (py::isinstance<py::dict>(idx_n)) {
|
||||
py::dict d = idx_n.cast<py::dict>();
|
||||
index_to_node_vec.resize(py::len(d));
|
||||
for (auto item : d) {
|
||||
index_to_node_vec[item.first.cast<int>()] = item.second.cast<py::object>();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
index_to_node_vec.resize(py::len(node_index));
|
||||
for (auto item : node_index) {
|
||||
index_to_node_vec[item.second.cast<int>()] = item.first.cast<py::object>();
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<int> target_ids;
|
||||
bool is_single_node = false;
|
||||
|
||||
if (v.is_none()) {
|
||||
target_ids.resize(N);
|
||||
for (int i = 0; i < N; ++i) target_ids[i] = i + 1;
|
||||
} else if (py::isinstance<py::list>(v) || py::isinstance<py::tuple>(v) || py::isinstance<py::set>(v)) {
|
||||
py::iterable v_iterable = v.cast<py::iterable>();
|
||||
for (py::handle node : v_iterable) {
|
||||
if (node_index.contains(node)) {
|
||||
target_ids.push_back(node_index[node].cast<int>() + 1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (node_index.contains(v)) {
|
||||
is_single_node = true;
|
||||
target_ids.push_back(node_index[v].cast<int>() + 1);
|
||||
} else {
|
||||
throw py::value_error("Node not found in graph.");
|
||||
}
|
||||
}
|
||||
|
||||
int num_targets = target_ids.size();
|
||||
if (num_targets == 0) return py::dict();
|
||||
|
||||
std::vector<double> ecc_values(num_targets, -1.0);
|
||||
bool is_connected = true;
|
||||
|
||||
{
|
||||
py::gil_scoped_release release;
|
||||
|
||||
#pragma omp parallel for schedule(dynamic)
|
||||
for (int i = 0; i < num_targets; i++) {
|
||||
if (!is_connected) continue;
|
||||
int u = target_ids[i];
|
||||
|
||||
double ecc_val = _bfs_eccentricity(G_l, u);
|
||||
|
||||
if (ecc_val < 0) {
|
||||
#pragma omp critical
|
||||
is_connected = false;
|
||||
} else {
|
||||
ecc_values[i] = ecc_val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_connected) {
|
||||
std::string msg = is_directed ?
|
||||
"Found infinite path length because the digraph is not strongly connected" :
|
||||
"Found infinite path length because the graph is not connected";
|
||||
throw py::value_error(msg);
|
||||
}
|
||||
|
||||
if (is_single_node) {
|
||||
return py::cast(ecc_values[0]);
|
||||
} else {
|
||||
py::dict result;
|
||||
for (int i = 0; i < num_targets; i++) {
|
||||
int internal_id = target_ids[i];
|
||||
py::object original_node = index_to_node_vec[internal_id - 1];
|
||||
result[original_node] = ecc_values[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,314 @@
|
||||
#include "mst.h"
|
||||
#ifdef _OPENMP
|
||||
#include <omp.h>
|
||||
#endif
|
||||
#include <cmath>
|
||||
|
||||
#include "../../classes/graph.h"
|
||||
#include "../../common/utils.h"
|
||||
|
||||
UnionFind::UnionFind() {}
|
||||
|
||||
UnionFind::UnionFind(std::vector<node_t> elements) {
|
||||
for (node_t x : elements) {
|
||||
parents[x] = x;
|
||||
weights[x] = 1;
|
||||
}
|
||||
}
|
||||
node_t UnionFind::operator[](node_t object) {
|
||||
if (!parents.count(object)) {
|
||||
parents[object] = object;
|
||||
weights[object] = 1;
|
||||
return object;
|
||||
}
|
||||
|
||||
std::vector<node_t> path;
|
||||
path.push_back(object);
|
||||
node_t root = parents[object];
|
||||
while (root != path.back()) {
|
||||
path.push_back(root);
|
||||
root = parents[root];
|
||||
}
|
||||
for (node_t ancestor : path) {
|
||||
parents[ancestor] = root;
|
||||
}
|
||||
return root;
|
||||
}
|
||||
|
||||
void UnionFind::_union(node_t object1, node_t object2) {
|
||||
node_t root, r;
|
||||
object1 = (*this)[object1];
|
||||
object2 = (*this)[object2];
|
||||
if (weights[object1] < weights[object2]) {
|
||||
root = object1, r = object2;
|
||||
} else {
|
||||
root = object2, r = object1;
|
||||
}
|
||||
weights[root] += weights[r];
|
||||
parents[r] = root;
|
||||
}
|
||||
|
||||
struct mst_Edge {
|
||||
double wt;
|
||||
node_t start_node, end_node;
|
||||
edge_attr_dict_factory edge_attr;
|
||||
mst_Edge(double wt, node_t start_node, node_t end_node, edge_attr_dict_factory edge_attr) {
|
||||
this->wt = wt;
|
||||
this->start_node = start_node;
|
||||
this->end_node = end_node;
|
||||
this->edge_attr = edge_attr;
|
||||
}
|
||||
};
|
||||
|
||||
py::object kruskal_mst_edges(py::object G, py::object minimum, py::object weight, py::object data, py::object ignore_nan) {
|
||||
UnionFind subtrees;
|
||||
Graph G_ = G.cast<Graph&>();
|
||||
std::string weight_key = weight_to_string(weight);
|
||||
std::vector<std::pair<weight_t, graph_edge>> edges;
|
||||
int sign = minimum.cast<py::bool_>().equal(py::cast(true)) ? 1 : -1;
|
||||
for (graph_edge& edge : G_._get_edges()) {
|
||||
weight_t wt = (edge.attr.count(weight_key) ? edge.attr[weight_key] : 1) * sign;
|
||||
if (!ignore_nan.cast<py::bool_>() && isnan(wt)) {
|
||||
PyErr_Format(PyExc_ValueError, "NaN found as an edge weight. Edge (%R, %R, %R)", G_.id_to_node[py::cast(edge.u)].ptr(), G_.id_to_node[py::cast(edge.v)].ptr(), attr_to_dict(edge.attr).ptr());
|
||||
return py::none();
|
||||
}
|
||||
edges.emplace_back(wt, edge);
|
||||
}
|
||||
std::sort(edges.begin(), edges.end(), [](const std::pair<weight_t, graph_edge>& edge1, const std::pair<weight_t, graph_edge>& edge2) -> bool {
|
||||
return edge1.first < edge2.first;
|
||||
});
|
||||
py::list ret;
|
||||
for (const auto& edge : edges) {
|
||||
node_t u = edge.second.u, v = edge.second.v;
|
||||
if (subtrees[u] != subtrees[v]) {
|
||||
if (data.cast<bool>()) {
|
||||
ret.append(py::make_tuple(G_.id_to_node[py::cast(u)], G_.id_to_node[py::cast(v)], attr_to_dict(edge.second.attr)));
|
||||
} else {
|
||||
ret.append(py::make_tuple(G_.id_to_node[py::cast(u)], G_.id_to_node[py::cast(v)]));
|
||||
}
|
||||
subtrees._union(u, v);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
|
||||
struct cmp {
|
||||
bool operator()(const mst_Edge& node1, const mst_Edge& node2) {
|
||||
return node1.wt > node2.wt;
|
||||
}
|
||||
};
|
||||
|
||||
py::object prim_mst_edges(py::object G, py::object minimum, py::object weight, py::object data, py::object ignore_nan) {
|
||||
Graph& G_ = G.cast<Graph&>();
|
||||
py::list res = py::list();
|
||||
node_dict_factory nodes_list = G_.node;
|
||||
std::unordered_set<node_t> nodes;
|
||||
for (node_dict_factory::iterator iter = nodes_list.begin(); iter != nodes_list.end(); iter++) {
|
||||
node_t node_id = iter->first;
|
||||
nodes.emplace(node_id);
|
||||
}
|
||||
int sign = 1;
|
||||
if (!minimum.cast<py::bool_>().equal(py::cast(true))) {
|
||||
sign = -1;
|
||||
}
|
||||
while (!nodes.empty()) {
|
||||
const node_t u = *(nodes.begin());
|
||||
nodes.erase(nodes.begin());
|
||||
std::priority_queue<mst_Edge, std::vector<mst_Edge>, cmp> frontier;
|
||||
std::unordered_map<node_t, bool> visited;
|
||||
node_t u_ = u;
|
||||
visited.emplace(u_, true);
|
||||
adj_attr_dict_factory u_neighbors = G_.adj[u];
|
||||
for (adj_attr_dict_factory::iterator i = u_neighbors.begin(); i != u_neighbors.end(); i++) {
|
||||
node_t v = i->first;
|
||||
edge_attr_dict_factory d = i->second;
|
||||
double wt = sign;
|
||||
if (d.find(py::cast<std::string>(weight)) != d.end()) {
|
||||
wt = d[py::cast<std::string>(weight)] * sign;
|
||||
}
|
||||
if (isnan(wt)) {
|
||||
if (ignore_nan.cast<bool>()) {
|
||||
continue;
|
||||
}
|
||||
PyErr_Format(PyExc_ValueError, "NaN found as an edge weight. Edge {(%R %R %R)}", (G_.id_to_node.attr("get")(u)).ptr(), G_.id_to_node.attr("get")(v).ptr(), attr_to_dict(d).ptr());
|
||||
return py::none();
|
||||
}
|
||||
frontier.push(mst_Edge(wt, u_, v, d));
|
||||
}
|
||||
while (!frontier.empty()) {
|
||||
mst_Edge node = frontier.top();
|
||||
frontier.pop();
|
||||
double W = node.wt;
|
||||
node_t u_id = node.start_node;
|
||||
node_t v_id = node.end_node;
|
||||
edge_attr_dict_factory d = node.edge_attr;
|
||||
if (visited.find(v_id) != visited.end() || nodes.find(v_id) == nodes.end()) {
|
||||
continue;
|
||||
}
|
||||
if (data.cast<bool>()) {
|
||||
res.append(py::make_tuple(G_.id_to_node.attr("get")(u_id), G_.id_to_node.attr("get")(v_id), attr_to_dict(d)));
|
||||
} else {
|
||||
res.append(py::make_tuple(G_.id_to_node.attr("get")(u_id), G_.id_to_node.attr("get")(v_id)));
|
||||
}
|
||||
visited.emplace(v_id, true);
|
||||
nodes.erase(v_id);
|
||||
adj_attr_dict_factory v_neighbors = G_.adj[v_id];
|
||||
for (adj_attr_dict_factory::iterator j = v_neighbors.begin(); j != v_neighbors.end(); j++) {
|
||||
node_t w = j->first;
|
||||
edge_attr_dict_factory d2 = j->second;
|
||||
if (visited.find(w) != visited.end()) {
|
||||
continue;
|
||||
}
|
||||
double new_weight = sign;
|
||||
if (d2.find(py::cast<std::string>(weight)) != d2.end()) {
|
||||
new_weight = d2[py::cast<std::string>(weight)] * sign;
|
||||
}
|
||||
frontier.push(mst_Edge(new_weight, v_id, w, d2));
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
struct CompactEdge {
|
||||
int u, v, id;
|
||||
double wt;
|
||||
};
|
||||
|
||||
struct AtomicBest {
|
||||
std::atomic<int> edge_idx;
|
||||
AtomicBest() : edge_idx(-1) {}
|
||||
AtomicBest(const AtomicBest& other) : edge_idx(other.edge_idx.load()) {}
|
||||
};
|
||||
|
||||
struct IntUnionFind {
|
||||
std::vector<int> parent;
|
||||
std::vector<int> rank;
|
||||
int component_count;
|
||||
|
||||
IntUnionFind(int n) : parent(n), rank(n, 0), component_count(n) {
|
||||
for (int i = 0; i < n; i++) parent[i] = i;
|
||||
}
|
||||
|
||||
int find(int i) {
|
||||
if (parent[i] == i) return i;
|
||||
return parent[i] = find(parent[i]);
|
||||
}
|
||||
|
||||
int find_readonly(int i) const {
|
||||
while (i != parent[i]) {
|
||||
i = parent[i];
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
bool unite(int i, int j) {
|
||||
int root_i = find(i);
|
||||
int root_j = find(j);
|
||||
if (root_i != root_j) {
|
||||
if (rank[root_i] < rank[root_j]) parent[root_i] = root_j;
|
||||
else if (rank[root_i] > rank[root_j]) parent[root_j] = root_i;
|
||||
else { parent[root_i] = root_j; rank[root_j]++; }
|
||||
component_count--;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
py::object boruvka_mst_edges(py::object G, py::object minimum, py::object weight, py::object data, py::object ignore_nan) {
|
||||
Graph& G_ = G.cast<Graph&>();
|
||||
std::string weight_key = weight_to_string(weight);
|
||||
int sign = minimum.cast<bool>() ? 1 : -1;
|
||||
bool return_data = data.cast<bool>();
|
||||
bool ignore_n = ignore_nan.cast<bool>();
|
||||
|
||||
std::shared_ptr<COOGraph> coo = G_.gen_COO(weight_key);
|
||||
int num_nodes = coo->nodes.size();
|
||||
int num_edges = coo->row.size();
|
||||
|
||||
if (num_nodes == 0) return py::list();
|
||||
|
||||
std::vector<CompactEdge> active_edges;
|
||||
active_edges.reserve(num_edges);
|
||||
|
||||
const auto& W_vec = *(coo->W_map[weight_key]);
|
||||
|
||||
for (int i = 0; i < num_edges; ++i) {
|
||||
double wt = W_vec[i] * sign;
|
||||
if (std::isnan(wt)) {
|
||||
if (!ignore_n) {
|
||||
PyErr_Format(PyExc_ValueError, "NaN found as an edge weight.");
|
||||
return py::none();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
active_edges.push_back({coo->row[i], coo->col[i], i, wt});
|
||||
}
|
||||
|
||||
IntUnionFind uf(num_nodes);
|
||||
std::vector<bool> in_mst(num_edges, false);
|
||||
{
|
||||
py::gil_scoped_release release;
|
||||
|
||||
while (uf.component_count > 1) {
|
||||
std::vector<AtomicBest> best_at(num_nodes);
|
||||
bool changed = false;
|
||||
|
||||
#pragma omp parallel for
|
||||
for (int i = 0; i < (int)active_edges.size(); ++i) {
|
||||
int root_u = uf.find_readonly(active_edges[i].u);
|
||||
int root_v = uf.find_readonly(active_edges[i].v);
|
||||
|
||||
if (root_u != root_v) {
|
||||
auto update_best = [&](int root, int edge_idx) {
|
||||
int current = best_at[root].edge_idx.load(std::memory_order_relaxed);
|
||||
while (current == -1 || active_edges[edge_idx].wt < active_edges[current].wt) {
|
||||
if (best_at[root].edge_idx.compare_exchange_weak(current, edge_idx)) break;
|
||||
}
|
||||
};
|
||||
update_best(root_u, i);
|
||||
update_best(root_v, i);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < num_nodes; ++i) {
|
||||
int e_idx = best_at[i].edge_idx.load();
|
||||
if (e_idx != -1) {
|
||||
const auto& e = active_edges[e_idx];
|
||||
if (uf.unite(e.u, e.v)) {
|
||||
in_mst[e.id] = true;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!changed) break;
|
||||
|
||||
auto new_end = std::remove_if(active_edges.begin(), active_edges.end(), [&](const CompactEdge& e) {
|
||||
return uf.find(e.u) == uf.find(e.v);
|
||||
});
|
||||
active_edges.erase(new_end, active_edges.end());
|
||||
}
|
||||
}
|
||||
|
||||
py::list ret;
|
||||
for (int i = 0; i < num_edges; ++i) {
|
||||
if (in_mst[i]) {
|
||||
node_t u = coo->nodes[coo->row[i]];
|
||||
node_t v = coo->nodes[coo->col[i]];
|
||||
|
||||
py::object u_obj = G_.id_to_node[py::cast(u)];
|
||||
py::object v_obj = G_.id_to_node[py::cast(v)];
|
||||
|
||||
if (return_data) {
|
||||
const auto& edge_attr = G_.adj.at(u).at(v);
|
||||
ret.append(py::make_tuple(u_obj, v_obj, attr_to_dict(edge_attr)));
|
||||
} else {
|
||||
ret.append(py::make_tuple(u_obj, v_obj));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../common/common.h"
|
||||
|
||||
class UnionFind {
|
||||
public:
|
||||
UnionFind();
|
||||
UnionFind(std::vector<node_t> elements);
|
||||
node_t operator[](node_t object);
|
||||
void _union(node_t object1, node_t object2);
|
||||
|
||||
private:
|
||||
std::unordered_map<node_t, node_t> parents;
|
||||
std::unordered_map<node_t, unsigned int> weights;
|
||||
};
|
||||
|
||||
py::object kruskal_mst_edges(py::object G, py::object minimum, py::object weight, py::object data, py::object ignore_nan);
|
||||
py::object prim_mst_edges(py::object G, py::object minimum, py::object weight, py::object data, py::object ignore_nan);
|
||||
py::object boruvka_mst_edges(py::object G, py::object minimum, py::object weight, py::object data, py::object ignore_nan);
|
||||
@@ -0,0 +1,376 @@
|
||||
#include "path.h"
|
||||
|
||||
#ifdef _OPENMP
|
||||
#include <omp.h>
|
||||
#endif
|
||||
#ifdef EASYGRAPH_ENABLE_GPU
|
||||
#include <gpu_easygraph.h>
|
||||
#endif
|
||||
|
||||
#include "../../classes/graph.h"
|
||||
#include "../../common/utils.h"
|
||||
#include "../../classes/linkgraph.h"
|
||||
#include "../../classes/segment_tree.cpp"
|
||||
|
||||
|
||||
#include <vector>
|
||||
#include <queue>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
|
||||
|
||||
std::vector<double> _dijkstra(const Graph_L& G_l, int source, int target) {
|
||||
int N = G_l.n;
|
||||
const double INF = std::numeric_limits<double>::infinity();
|
||||
std::vector<double> dis(N + 1, INF);
|
||||
std::priority_queue<std::pair<double, int>,
|
||||
std::vector<std::pair<double, int>>,
|
||||
std::greater<std::pair<double, int>>> pq;
|
||||
|
||||
dis[source] = 0.0;
|
||||
pq.push({0.0, source});
|
||||
|
||||
const std::vector<int>& head = G_l.head;
|
||||
const std::vector<LinkEdge>& E = G_l.edges;
|
||||
|
||||
while (!pq.empty()) {
|
||||
std::pair<double, int> top = pq.top();
|
||||
pq.pop();
|
||||
|
||||
double d = top.first;
|
||||
int u = top.second;
|
||||
|
||||
//Lazy deletion
|
||||
if (d > dis[u]) continue;
|
||||
|
||||
// cutoff
|
||||
if (u == target) break;
|
||||
|
||||
for (int p = head[u]; p != -1; p = E[p].next) {
|
||||
int v = E[p].to;
|
||||
double w = static_cast<double>(E[p].w);
|
||||
|
||||
if (dis[u] + w < dis[v]) {
|
||||
dis[v] = dis[u] + w;
|
||||
pq.push({dis[v], v});
|
||||
}
|
||||
}
|
||||
}
|
||||
return dis;
|
||||
}
|
||||
|
||||
|
||||
py::object _invoke_cpp_dijkstra_multisource(py::object G, py::object sources, py::object weight, py::object target) {
|
||||
bool is_directed = G.attr("is_directed")().cast<bool>();
|
||||
Graph& G_ = G.cast<Graph&>();
|
||||
std::string weight_key = weight_to_string(weight);
|
||||
|
||||
Graph_L G_l;
|
||||
if(G_.linkgraph_dirty){
|
||||
G_l = graph_to_linkgraph(G_, is_directed, weight_key, true, false);
|
||||
G_.linkgraph_structure = G_l;
|
||||
G_.linkgraph_dirty = false;
|
||||
} else {
|
||||
G_l = G_.linkgraph_structure;
|
||||
}
|
||||
|
||||
node_t target_id = -1;
|
||||
if (!target.is_none()) {
|
||||
target_id = G_.node_to_id.attr("get")(target, -1).cast<node_t>();
|
||||
}
|
||||
|
||||
py::list sources_list = py::list(sources);
|
||||
int num_sources = py::len(sources_list);
|
||||
int N = G_l.n;
|
||||
|
||||
std::vector<node_t> source_ids(num_sources);
|
||||
for(int i = 0; i < num_sources; i++){
|
||||
if(G_.node_to_id.attr("get")(sources_list[i], py::none()).is(py::none())){
|
||||
printf("The node should exist in the graph!");
|
||||
return py::none();
|
||||
}
|
||||
source_ids[i] = G_.node_to_id.attr("get")(sources_list[i]).cast<node_t>();
|
||||
}
|
||||
std::vector<double> results(num_sources * N);
|
||||
{
|
||||
py::gil_scoped_release release;
|
||||
|
||||
#pragma omp parallel for schedule(dynamic)
|
||||
for (int i = 0; i < num_sources; i++) {
|
||||
node_t s = source_ids[i];
|
||||
|
||||
std::vector<double> dists = _dijkstra(G_l, s, target_id);
|
||||
|
||||
size_t offset = (size_t)i * N;
|
||||
for (int j = 1; j <= N; j++) {
|
||||
results[offset + (j - 1)] = dists[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
py::array::ShapeContainer ret_shape{num_sources, N};
|
||||
py::array_t<double> ret(ret_shape, results.data());
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef EASYGRAPH_ENABLE_GPU
|
||||
py::object _invoke_gpu_dijkstra_multisource(py::object G,py::object py_sources, py::object weight, py::object target) {
|
||||
Graph& G_ = G.cast<Graph&>();
|
||||
if (weight.is_none()) {
|
||||
G_.gen_CSR();
|
||||
} else {
|
||||
G_.gen_CSR(weight_to_string(weight));
|
||||
}
|
||||
auto csr_graph = G_.csr_graph;
|
||||
std::vector<int>& E = csr_graph->E;
|
||||
std::vector<int>& V = csr_graph->V;
|
||||
std::vector<double> *W_p = weight.is_none() ? &(csr_graph->unweighted_W)
|
||||
: csr_graph->W_map.find(weight_to_string(weight))->second.get();
|
||||
auto sources = G_.gen_CSR_sources(py_sources);
|
||||
std::vector<double> sssp;
|
||||
int gpu_r = gpu_easygraph::sssp_dijkstra(V, E, *W_p, *sources,
|
||||
target.is_none() ? -1 : (int)py::cast<py::int_>(target),
|
||||
sssp);
|
||||
|
||||
if (gpu_r != gpu_easygraph::EG_GPU_SUCC) {
|
||||
// the code below will throw an exception
|
||||
py::pybind11_fail(gpu_easygraph::err_code_detail(gpu_r));
|
||||
}
|
||||
|
||||
py::array::ShapeContainer ret_shape{(int)sources->size(), (int)V.size() - 1};
|
||||
py::array_t<double> ret(ret_shape, sssp.data());
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
py::object _dijkstra_multisource(py::object G,py::object sources, py::object weight, py::object target) {
|
||||
#ifdef EASYGRAPH_ENABLE_GPU
|
||||
return _invoke_gpu_dijkstra_multisource(G, sources, weight, target);
|
||||
#else
|
||||
return _invoke_cpp_dijkstra_multisource(G, sources, weight, target);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
py::object _spfa(py::object G, py::object source, py::object weight) {
|
||||
Graph& G_ = G.cast<Graph&>();
|
||||
bool is_directed = G.attr("is_directed")().cast<bool>();
|
||||
std::string weight_key = weight_to_string(weight);
|
||||
Graph_L G_l = graph_to_linkgraph(G_, is_directed,weight_key, false);
|
||||
int N = G_.node.size();
|
||||
|
||||
std::vector<int> Q(N+10,0);
|
||||
std::vector<double> dis(N+1,INFINITY);
|
||||
std::vector<bool> vis(N+1,false);
|
||||
|
||||
int l = 0, r = 1;
|
||||
node_t S = G_.node_to_id[source].cast<node_t>();
|
||||
Q[0] = S; vis[S] = true; dis[S] = 0;
|
||||
std::vector<LinkEdge>& E = G_l.edges;
|
||||
|
||||
std::vector<int>& head = G_l.head;
|
||||
while (l != r) {
|
||||
if (r != 0 && dis[Q[l]] >= dis[Q[r - 1]])
|
||||
std::swap(Q[l], Q[r - 1]);
|
||||
int u = Q[l++];
|
||||
if (l >= N) l -= N;
|
||||
vis[u] = true;
|
||||
|
||||
for(int p = head[u]; p != -1; p = E[p].next) {
|
||||
int v=E[p].to;
|
||||
if (dis[v]>dis[u]+E[p].w) {
|
||||
dis[v]=dis[u]+E[p].w;
|
||||
if (!vis[v]) {
|
||||
vis[v]=true;
|
||||
if (l == 0 || dis[v] >= dis[Q[l]])
|
||||
Q[r++]=v;
|
||||
else
|
||||
Q[--l]=v;
|
||||
if (r >= N) r -= N;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
py::list pydist = py::list();
|
||||
for(int i = 1; i <= N; i++){
|
||||
pydist.append(py::cast(dis[i]));
|
||||
}
|
||||
return pydist;
|
||||
}
|
||||
|
||||
|
||||
py::object Prim(py::object G, py::object weight) {
|
||||
std::unordered_map<node_t, std::unordered_map<node_t, weight_t>> res_dict;
|
||||
py::dict result_dict = py::dict();
|
||||
Graph& G_ = G.cast<Graph&>();
|
||||
adj_dict_factory adj = G_.adj;
|
||||
std::vector<node_t> selected;
|
||||
std::vector<node_t> candidate;
|
||||
node_dict_factory& node_list = G_.node;
|
||||
std::string weight_key = weight_to_string(weight);
|
||||
for (node_dict_factory::iterator i = node_list.begin(); i != node_list.end(); i++) {
|
||||
node_t node_id = i->first;
|
||||
result_dict[G_.id_to_node[py::cast(node_id)]] = py::dict();
|
||||
if (selected.size() == 0) {
|
||||
selected.emplace_back(node_id);
|
||||
} else {
|
||||
candidate.emplace_back(node_id);
|
||||
}
|
||||
}
|
||||
while (candidate.size() > 0) {
|
||||
node_t start_id = -1;
|
||||
node_t end_id = -1;
|
||||
weight_t min_weight = INFINITY;
|
||||
int selected_len = selected.size();
|
||||
int candidate_len = candidate.size();
|
||||
for (int i = 0; i < selected_len; i++) {
|
||||
for (int j = 0; j < candidate_len; j++) {
|
||||
adj_attr_dict_factory node_adj = G_.adj[selected[i]];
|
||||
edge_attr_dict_factory edge_attr;
|
||||
weight_t edge_weight = INFINITY;
|
||||
bool j_exist = false;
|
||||
if (node_adj.find(candidate[j]) != node_adj.end()) {
|
||||
edge_attr = node_adj[candidate[j]];
|
||||
edge_weight = edge_attr.find(weight_key) != edge_attr.end() ? edge_attr[weight_key] : 1;
|
||||
j_exist = true;
|
||||
}
|
||||
if ((node_list.find(selected[i]) != node_list.end()) &&
|
||||
j_exist &&
|
||||
(edge_weight < min_weight)) {
|
||||
start_id = selected[i];
|
||||
end_id = candidate[j];
|
||||
min_weight = edge_weight;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (start_id != -1 && end_id != -1) {
|
||||
res_dict[start_id][end_id] = min_weight;
|
||||
selected.emplace_back(end_id);
|
||||
std::vector<node_t>::iterator temp_iter;
|
||||
temp_iter = std::find(candidate.begin(), candidate.end(), end_id);
|
||||
candidate.erase(temp_iter);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (std::unordered_map<node_t, std::unordered_map<node_t, weight_t>>::iterator k = res_dict.begin();
|
||||
k != res_dict.end(); k++) {
|
||||
py::object res_node = G_.id_to_node[py::cast(k->first)];
|
||||
for (std::unordered_map<node_t, weight_t>::iterator z = k->second.begin(); z != k->second.end(); z++) {
|
||||
py::object res_adj_node = G_.id_to_node[py::cast(z->first)];
|
||||
result_dict[res_node][res_adj_node] = z->second;
|
||||
}
|
||||
}
|
||||
return result_dict;
|
||||
}
|
||||
bool comp(const std::pair<std::pair<node_t, node_t>, weight_t>& a, const std::pair<std::pair<node_t, node_t>, weight_t>& b) {
|
||||
return a.second < b.second;
|
||||
}
|
||||
py::object Kruskal(py::object G, py::object weight) {
|
||||
std::unordered_map<node_t, std::unordered_map<node_t, weight_t>> res_dict;
|
||||
py::dict result_dict = py::dict();
|
||||
std::vector<std::vector<node_t>> group;
|
||||
Graph& G_ = G.cast<Graph&>();
|
||||
adj_dict_factory& adj = G_.adj;
|
||||
node_dict_factory& node_list = G_.node;
|
||||
std::vector<std::pair<std::pair<node_t, node_t>, weight_t>> edge_list;
|
||||
std::string weight_key = weight_to_string(weight);
|
||||
for (node_dict_factory::iterator i = node_list.begin(); i != node_list.end(); i++) {
|
||||
node_t i_id = i->first;
|
||||
result_dict[G_.id_to_node[py::cast(i_id)]] = py::dict();
|
||||
std::vector<node_t> temp_vector;
|
||||
temp_vector.emplace_back(i_id);
|
||||
group.emplace_back(temp_vector);
|
||||
adj_attr_dict_factory i_adj = adj[i_id];
|
||||
for (adj_attr_dict_factory::iterator j = i_adj.begin(); j != i_adj.end(); j++) {
|
||||
node_t j_id = j->first;
|
||||
weight_t edge_weight = adj[i_id][j_id].find(weight_key) != adj[i_id][j_id].end() ? adj[i_id][j_id][weight_key] : 1;
|
||||
edge_list.emplace_back(std::make_pair(std::make_pair(i_id, j_id), edge_weight));
|
||||
}
|
||||
}
|
||||
std::sort(edge_list.begin(), edge_list.end(), comp);
|
||||
node_t m, n;
|
||||
int group_size = group.size();
|
||||
for (auto edge : edge_list) {
|
||||
for (int i = 0; i < group_size; i++) {
|
||||
int group_i_size = group[i].size();
|
||||
for (int j = 0; j < group_i_size; j++) {
|
||||
if (group[i][j] == edge.first.first) {
|
||||
m = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (int j = 0; j < group_i_size; j++) {
|
||||
if (group[i][j] == edge.first.second) {
|
||||
n = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (m != n) {
|
||||
res_dict[edge.first.first][edge.first.second] = edge.second;
|
||||
std::vector<node_t> temp_vector;
|
||||
group[m].insert(group[m].end(), group[n].begin(), group[n].end());
|
||||
group[n].clear();
|
||||
}
|
||||
}
|
||||
for (std::unordered_map<node_t, std::unordered_map<node_t, weight_t>>::iterator k = res_dict.begin();
|
||||
k != res_dict.end(); k++) {
|
||||
py::object res_node = G_.id_to_node[py::cast(k->first)];
|
||||
for (std::unordered_map<node_t, weight_t>::iterator z = k->second.begin(); z != k->second.end(); z++) {
|
||||
py::object res_adj_node = G_.id_to_node[py::cast(z->first)];
|
||||
result_dict[res_node][res_adj_node] = z->second;
|
||||
}
|
||||
}
|
||||
return result_dict;
|
||||
}
|
||||
|
||||
py::object Floyd(py::object G, py::object weight) {
|
||||
std::unordered_map<node_t, std::unordered_map<node_t, weight_t>> res_dict;
|
||||
Graph& G_ = G.cast<Graph&>();
|
||||
adj_dict_factory& adj = G_.adj;
|
||||
py::dict result_dict = py::dict();
|
||||
node_dict_factory& node_list = G_.node;
|
||||
std::string weight_key = weight_to_string(weight);
|
||||
for (node_dict_factory::iterator i = node_list.begin(); i != node_list.end(); i++) {
|
||||
result_dict[G_.id_to_node[py::cast(i->first)]] = py::dict();
|
||||
adj_attr_dict_factory temp_key = adj[i->first];
|
||||
for (node_dict_factory::iterator j = node_list.begin(); j != node_list.end(); j++) {
|
||||
if (temp_key.find(j->first) != temp_key.end()) {
|
||||
if (adj[i->first][j->first].count(weight_key) == 0) {
|
||||
adj[i->first][j->first][weight_key] = 1;
|
||||
}
|
||||
res_dict[i->first][j->first] = adj[i->first][j->first][weight_key];
|
||||
} else {
|
||||
res_dict[i->first][j->first] = INFINITY;
|
||||
}
|
||||
if (i->first == j->first) {
|
||||
res_dict[i->first][i->first] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (node_dict_factory::iterator k = node_list.begin(); k != node_list.end(); k++) {
|
||||
for (node_dict_factory::iterator i = node_list.begin(); i != node_list.end(); i++) {
|
||||
for (node_dict_factory::iterator j = node_list.begin(); j != node_list.end(); j++) {
|
||||
weight_t temp = res_dict[i->first][k->first] + res_dict[k->first][j->first];
|
||||
weight_t i_j_weight = res_dict[i->first][j->first];
|
||||
if (i_j_weight > temp) {
|
||||
res_dict[i->first][j->first] = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (std::unordered_map<node_t, std::unordered_map<node_t, weight_t>>::iterator k = res_dict.begin();
|
||||
k != res_dict.end(); k++) {
|
||||
py::object res_node = G_.id_to_node[py::cast(k->first)];
|
||||
for (std::unordered_map<node_t, weight_t>::iterator z = k->second.begin(); z != k->second.end(); z++) {
|
||||
py::object res_adj_node = G_.id_to_node[py::cast(z->first)];
|
||||
result_dict[res_node][res_adj_node] = z->second;
|
||||
}
|
||||
}
|
||||
return result_dict;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../common/common.h"
|
||||
|
||||
py::object _dijkstra_multisource(py::object G, py::object sources, py::object weight, py::object target);
|
||||
// py::object _dijkstra(py::object G, py::object sources, py::object weight, py::object target);
|
||||
py::object _spfa(py::object G, py::object source, py::object weight);
|
||||
py::object Floyd(py::object G,py::object weight);
|
||||
py::object Prim(py::object G,py::object weight);
|
||||
py::object Kruskal(py::object G,py::object weight);
|
||||
py::object average_shortest_path_length(py::object G, py::object weight, py::object method);
|
||||
py::object eccentricity(py::object G, py::object v, py::object sp);
|
||||
Reference in New Issue
Block a user