chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "biconnected.h"
|
||||
#include "connected.h"
|
||||
#include "strongly_connected.h"
|
||||
@@ -0,0 +1,109 @@
|
||||
#include "biconnected.h"
|
||||
#include "../../classes/graph.h"
|
||||
#include "../../common/utils.h"
|
||||
|
||||
|
||||
node_t index_edge(std::vector<std::pair<node_t, node_t>>& edges, const std::pair<node_t, node_t>& target) {
|
||||
for (int i = edges.size() - 1;i >= 0;i--) {
|
||||
if ((edges[i].first == target.first) && (edges[i].second == target.second)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
py::object _biconnected_dfs_record_edges(py::object G, py::object need_components) {
|
||||
py::list ret = py::list();
|
||||
std::unordered_set<node_t> visited;
|
||||
Graph& G_ = G.cast<Graph&>();
|
||||
node_dict_factory &nodes_list = G_.node;
|
||||
for (node_dict_factory::iterator iter = nodes_list.begin();iter != nodes_list.end();iter++) {
|
||||
node_t start_id = iter->first;
|
||||
if (visited.find(start_id) != visited.end()) {
|
||||
continue;
|
||||
}
|
||||
std::unordered_map<node_t, int> discovery;
|
||||
std::unordered_map<node_t, int> low;
|
||||
node_t root_children = 0;
|
||||
discovery.emplace(start_id, 0);
|
||||
low.emplace(start_id, 0);
|
||||
visited.emplace(start_id);
|
||||
std::vector<std::pair<node_t, node_t>> edge_stack;
|
||||
std::vector<stack_node> stack;
|
||||
adj_attr_dict_factory& start_adj = G_.adj[start_id];
|
||||
NeighborIterator neighbors_iter = NeighborIterator(start_adj);
|
||||
stack_node initial_stack_node(start_id, start_id, neighbors_iter);
|
||||
stack.emplace_back(initial_stack_node);
|
||||
while (!stack.empty()) {
|
||||
stack_node& node_info = stack.back();
|
||||
node_t node_grandparent_id = node_info.grandparent;
|
||||
node_t node_parent_id = node_info.parent;
|
||||
try {
|
||||
node_t node_child_id = node_info.neighbors_iter.next();
|
||||
if (node_grandparent_id == node_child_id) {
|
||||
continue;
|
||||
}
|
||||
if (visited.find(node_child_id) != visited.end()) {
|
||||
if (discovery[node_child_id] <= discovery[node_parent_id]) {
|
||||
low[node_parent_id] = std::min(low[node_parent_id], discovery[node_child_id]);
|
||||
if (need_components.cast<bool>()) {
|
||||
edge_stack.emplace_back(std::make_pair(node_parent_id, node_child_id));
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
low[node_child_id] = discovery[node_child_id] = discovery.size();
|
||||
visited.emplace(node_child_id);
|
||||
adj_attr_dict_factory& node_child_adj = G_.adj[node_child_id];
|
||||
NeighborIterator child_neighbors_iter = NeighborIterator(G_.adj[node_child_id]);
|
||||
stack.emplace_back(node_parent_id, node_child_id, child_neighbors_iter);
|
||||
if (need_components.cast<bool>()) {
|
||||
edge_stack.emplace_back(std::make_pair(node_parent_id, node_child_id));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (int) {
|
||||
stack.pop_back();
|
||||
if (stack.size() > 1) {
|
||||
if (low[node_parent_id] >= discovery[node_grandparent_id]) {
|
||||
if (need_components.cast<bool>()) {
|
||||
py::list tmp_ret = py::list();
|
||||
std::pair<node_t, node_t> iter_edge = std::make_pair(-1, -1);
|
||||
while ((iter_edge.first != node_grandparent_id || iter_edge.second != node_parent_id)) {
|
||||
iter_edge = edge_stack.back();
|
||||
edge_stack.pop_back();
|
||||
tmp_ret.append(py::make_tuple(G_.id_to_node[py::cast(iter_edge.first)], G_.id_to_node[py::cast(iter_edge.second)]));
|
||||
}
|
||||
ret.append(tmp_ret);
|
||||
}
|
||||
else {
|
||||
ret.append(G_.id_to_node[py::cast(node_grandparent_id)]);
|
||||
}
|
||||
}
|
||||
low[node_grandparent_id] = std::min(low[node_grandparent_id], low[node_parent_id]);
|
||||
}
|
||||
else if (stack.size() > 0) {
|
||||
root_children += 1;
|
||||
if (need_components.cast<bool>()) {
|
||||
std::pair<node_t, node_t> target = std::make_pair(node_grandparent_id, node_parent_id);
|
||||
node_t ind = index_edge(edge_stack, target);
|
||||
if (ind != -1) {
|
||||
py::list tmp_ret = py::list();
|
||||
for (node_t z = ind;z < edge_stack.size();z++) {
|
||||
tmp_ret.append(py::make_tuple(G_.id_to_node[py::cast(edge_stack[z].first)], G_.id_to_node[py::cast(edge_stack[z].second)]));
|
||||
}
|
||||
ret.append(tmp_ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!need_components.cast<bool>()) {
|
||||
if (root_children > 1) {
|
||||
ret.append(G_.id_to_node(start_id));
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../common/common.h"
|
||||
|
||||
class NeighborIterator {
|
||||
public:
|
||||
NeighborIterator() {
|
||||
}
|
||||
NeighborIterator(adj_attr_dict_factory& neighbor_map) {
|
||||
now = neighbor_map.begin();;
|
||||
end = neighbor_map.end();
|
||||
}
|
||||
node_t next() {
|
||||
if (now == end) {
|
||||
throw -1;
|
||||
}
|
||||
else {
|
||||
return (now++)->first;
|
||||
}
|
||||
}
|
||||
private:
|
||||
adj_attr_dict_factory::iterator now, end;
|
||||
};
|
||||
typedef struct stackNode {
|
||||
node_t grandparent, parent;
|
||||
NeighborIterator neighbors_iter;
|
||||
stackNode(node_t grandparent, node_t parent, NeighborIterator neighbors_iter) {
|
||||
this->grandparent = grandparent;
|
||||
this->parent = parent;
|
||||
this->neighbors_iter = neighbors_iter;
|
||||
}
|
||||
}stack_node;
|
||||
|
||||
py::object _biconnected_dfs_record_edges(py::object G, py::object need_components);
|
||||
@@ -0,0 +1,205 @@
|
||||
#include "connected.h"
|
||||
|
||||
#include <pybind11/stl.h>
|
||||
|
||||
#include "../../classes/graph.h"
|
||||
#include "../../classes/directed_graph.h"
|
||||
#include "../../common/utils.h"
|
||||
#include "time.h"
|
||||
|
||||
#define gmin(x, y) x = x < y? x: y
|
||||
|
||||
py::object plain_bfs(py::object G, py::object source) {
|
||||
Graph& G_ = G.cast<Graph&>();
|
||||
node_t source_id = G_.node_to_id.attr("get")(source).cast<node_t>();
|
||||
adj_dict_factory& G_adj = G_.adj;
|
||||
std::unordered_set<node_t> seen;
|
||||
std::unordered_set<node_t> nextlevel;
|
||||
nextlevel.emplace(source_id);
|
||||
py::list res = py::list();
|
||||
while (nextlevel.size()) {
|
||||
std::unordered_set<node_t> thislevel = nextlevel;
|
||||
nextlevel = std::unordered_set<node_t>();
|
||||
for (std::unordered_set<node_t>::iterator i = thislevel.begin(); i != thislevel.end(); i++) {
|
||||
node_t v_id = *i;
|
||||
if (seen.find(v_id) == seen.end()) {
|
||||
seen.emplace(v_id);
|
||||
adj_attr_dict_factory& v_adj = G_adj[v_id];
|
||||
for (adj_attr_dict_factory::iterator j = v_adj.begin(); j != v_adj.end(); j++) {
|
||||
node_t neighbor_id = j->first;
|
||||
nextlevel.emplace(neighbor_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (std::unordered_set<node_t>::iterator i = seen.begin(); i != seen.end(); i++) {
|
||||
node_t res_id = *(i);
|
||||
res.append(G_.id_to_node.attr("get")(res_id));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
py::object connected_component_undirected(py::object G) {
|
||||
Graph& G_ = G.cast<Graph&>();
|
||||
bool is_directed = G.attr("is_directed")().cast<bool>();
|
||||
if (is_directed == true) {
|
||||
printf("connected_component_undirected is designed for undirected graphs.\n");
|
||||
return py::dict();
|
||||
}
|
||||
int N = G_.node.size();
|
||||
int M = G.attr("number_of_edges")().cast<int>();
|
||||
std::vector<Edge_weighted> E_res(N+5);
|
||||
for(int i=0; i < N+5; i++) {
|
||||
E_res[i].toward = 0;
|
||||
E_res[i].next = 0;
|
||||
}
|
||||
int edge_number_res = 0;
|
||||
std::vector<int> parent(N+5, 0);
|
||||
std::vector<int> rank_node(N+5, 0);
|
||||
std::vector<int> color(N+5, 0);
|
||||
std::vector<int> head_res(N+5, 0);
|
||||
std::vector<bool> has_edge(N+5, false);
|
||||
|
||||
|
||||
py::list nodes_list = py::list(G.attr("nodes"));
|
||||
for (int i = 0;i < py::len(nodes_list);i++) {
|
||||
node_t i_id = (G_.node_to_id[nodes_list[i]]).cast<node_t>();
|
||||
parent[i_id] = i_id;
|
||||
}
|
||||
|
||||
for (graph_edge& edge : G_._get_edges()) {
|
||||
node_t u = edge.u, v = edge.v;
|
||||
has_edge[u] = true; has_edge[v] = true;
|
||||
_union_node(u, v, parent, rank_node);
|
||||
}
|
||||
|
||||
int Tot = 0;
|
||||
for(int i = 1; i < N + 1; ++i) {
|
||||
if (!has_edge[i])
|
||||
continue;
|
||||
int fx = _getfa(i, parent);
|
||||
if(fx == i){
|
||||
color[++Tot] = fx;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 1; i < N + 1; ++i) {
|
||||
int fx = _getfa(i, parent);
|
||||
_add_edge_res(fx, i, E_res, head_res, &edge_number_res);
|
||||
}
|
||||
|
||||
py::dict ret = py::dict();
|
||||
for (int i = 1; i <= Tot; ++i) {
|
||||
py::list tmp = py::list();
|
||||
for(int p = head_res[color[i]]; p; p = E_res[p].next){
|
||||
tmp.append(py::cast(E_res[p].toward));
|
||||
}
|
||||
ret[py::cast(i)] = tmp;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline void _union_node(const int &u, const int &v, std::vector<int> &parent, std::vector<int> &rank_node) {
|
||||
int x = _getfa(u, parent), y = _getfa(v, parent); //先找到两个根节点
|
||||
if (rank_node[x] <= rank_node[y])
|
||||
parent[x] = y;
|
||||
else
|
||||
parent[y] = x;
|
||||
if (rank_node[x] == rank_node[y] && x != y)
|
||||
rank_node[y]++; //如果深度相同且根节点不同,则新的根节点的深度+1
|
||||
}
|
||||
|
||||
int _getfa(const int & x, std::vector<int> &parent) {
|
||||
int r,k,t;
|
||||
r=x;
|
||||
while(parent[r]!=r)
|
||||
r=parent[r];
|
||||
k=r;
|
||||
r=x;
|
||||
while(parent[r]!=k) {
|
||||
t=parent[r];
|
||||
parent[r]=k;
|
||||
r=t;
|
||||
}
|
||||
return k;
|
||||
}
|
||||
|
||||
py::object connected_component_directed(py::object G) {
|
||||
bool is_directed = G.attr("is_directed")().cast<bool>();
|
||||
if (is_directed == false) {
|
||||
printf("connected_component_directed is designed for directed graphs.\n");
|
||||
return py::list();
|
||||
}
|
||||
DiGraph& G_ = G.cast<DiGraph&>();
|
||||
int N = G_.node.size();
|
||||
Graph_L G_l;
|
||||
if(G_.linkgraph_dirty || G_.linkgraph_structure.max_deg == -1){
|
||||
G_l = graph_to_linkgraph(G_, is_directed, "", true, false);
|
||||
G_.linkgraph_dirty = false;
|
||||
}
|
||||
else{
|
||||
G_l = G_.linkgraph_structure;
|
||||
}
|
||||
std::vector<LinkEdge>& E = G_l.edges;
|
||||
std::vector<int> outDegree = G_l.degree;
|
||||
std::vector<int> head = G_l.head;
|
||||
|
||||
int Time = 0, cnt = 0, Tot = 0, edge_number_res = 0;
|
||||
|
||||
std::vector<int> dfn(N+5, 0);
|
||||
std::vector<int> low(N+5, 0);
|
||||
std::vector<int> st(N+5, 0);
|
||||
std::vector<int> color(N+5, 0);
|
||||
std::vector<int> head_res(N+5, 0);
|
||||
std::vector<bool> in_stack(N+5, false);
|
||||
std::vector<bool> has_edge(N+5, false);
|
||||
|
||||
std::vector<Edge_weighted> E_res(N+5);
|
||||
for(int i=0; i < N+5; i++) {
|
||||
E_res[i].toward = 0;
|
||||
E_res[i].next = 0;
|
||||
}
|
||||
|
||||
for (graph_edge& edge : G_._get_edges()) {
|
||||
node_t u = edge.u, v = edge.v;
|
||||
has_edge[u] = true; has_edge[v] = true;
|
||||
}
|
||||
|
||||
for (int i = 1; i < N + 1; ++i)
|
||||
if (!dfn[i] && has_edge[i])
|
||||
_tarjan(i, &Time, &cnt, &Tot, E, head, dfn, low, st, color, in_stack, E_res, head_res, &edge_number_res);
|
||||
|
||||
py::list ret = py::list();
|
||||
for (int i = 1; i <= Tot; ++i) {
|
||||
py::set tmp;
|
||||
for(int p = head_res[i]; p; p = E_res[p].next)
|
||||
tmp.add(G_.id_to_node.attr("get")(E_res[p].toward));
|
||||
ret.append(tmp);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void _add_edge_res(const int &u, const int &v, std::vector<Edge_weighted> &E_res, std::vector<int> &head_res, int *edge_number_res) {
|
||||
E_res[++(*edge_number_res)].next = head_res[u];
|
||||
E_res[*edge_number_res].toward = v;
|
||||
head_res[u] = *edge_number_res;
|
||||
}
|
||||
|
||||
void _tarjan(const int &u, int *Time, int *cnt, int *Tot, std::vector<LinkEdge>& E, std::vector<int>& head, std::vector<int> &dfn, std::vector<int> &low, std::vector<int> &st, std::vector<int> &color, std::vector<bool> &in_stack, std::vector<Edge_weighted> &E_res, std::vector<int> &head_res, int *edge_number_res) {
|
||||
dfn[u] = low[u] = ++(*Time); st[++(*cnt)] = u; in_stack[u] = true;
|
||||
for(int p = head[u]; p != -1; p = E[p].next){
|
||||
int v = E[p].to;
|
||||
if (!dfn[v]) _tarjan(v, Time, cnt, Tot, E, head, dfn, low, st, color, in_stack, E_res, head_res, edge_number_res), gmin(low[u], low[v]);
|
||||
else if (in_stack[v]) gmin(low[u], dfn[v]);
|
||||
}
|
||||
|
||||
if (dfn[u] == low[u]) {
|
||||
for (++(*Tot); st[*cnt] != u; --(*cnt)) {
|
||||
_add_edge_res(*Tot, st[*cnt], E_res, head_res, edge_number_res);
|
||||
in_stack[st[*cnt]] = false, color[st[*cnt]] = *Tot;
|
||||
}
|
||||
_add_edge_res(*Tot, st[*cnt], E_res, head_res, edge_number_res);
|
||||
in_stack[u] = false; color[u] = *Tot; --(*cnt);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../common/common.h"
|
||||
#include "../../classes/linkgraph.h"
|
||||
|
||||
struct Edge_weighted{
|
||||
int toward, next;
|
||||
};
|
||||
|
||||
py::object plain_bfs(py::object G, py::object source);
|
||||
py::object connected_component_undirected(py::object G);
|
||||
inline void _union_node(const int &u, const int &v, std::vector<int>& parent, std::vector<int> &rank_node);
|
||||
int _getfa(const int & x, std::vector<int> &parent);
|
||||
py::object connected_component_directed(py::object G);
|
||||
void _tarjan(const int &u, int *Time, int *cnt, int *Tot, std::vector<LinkEdge>& E, std::vector<int>& head, std::vector<int> &dfn, std::vector<int> &low, std::vector<int> &st, std::vector<int> &color, std::vector<bool> &in_stack, std::vector<Edge_weighted> &E_res, std::vector<int> &head_res, int *edge_number_res);
|
||||
void _add_edge_res(const int &u, const int &v, std::vector<Edge_weighted> &E_res, std::vector<int> &head_res, int *edge_number_res);
|
||||
@@ -0,0 +1,90 @@
|
||||
#include "strongly_connected.h"
|
||||
#include "connected.h"
|
||||
|
||||
#include "../../classes/directed_graph.h"
|
||||
|
||||
#define MAX_NODES_NUM4RECURSION_METHOD 100000
|
||||
|
||||
py::object strongly_connected_components(py::object G) {
|
||||
bool is_directed = G.attr("is_directed")().cast<bool>();
|
||||
if (is_directed == false) {
|
||||
printf("connected_component_directed is designed for directed graphs.\n");
|
||||
return py::list();
|
||||
}
|
||||
|
||||
int N = G.attr("number_of_nodes")().cast<int>();
|
||||
if(N < MAX_NODES_NUM4RECURSION_METHOD){
|
||||
return connected_component_directed(G);
|
||||
}
|
||||
|
||||
return strongly_connected_components_iteration_impl(G);
|
||||
}
|
||||
|
||||
py::object strongly_connected_components_iteration_impl(py::object G) {
|
||||
py::list res = py::list();
|
||||
DiGraph& G_ = py::cast<DiGraph&>(G);
|
||||
adj_dict_factory& adj = G_.adj;
|
||||
std::unordered_map<node_t, node_t> preorder;
|
||||
std::unordered_map<node_t, node_t> lowlink;
|
||||
std::set<node_t> scc_found;
|
||||
std::vector<node_t> scc_queue;
|
||||
int i = 0;
|
||||
node_dict_factory& nodes_list = G_.node;
|
||||
for (node_dict_factory::iterator source = nodes_list.begin(); source != nodes_list.end(); source++) {
|
||||
node_t source_id = source->first;
|
||||
if (scc_found.find(source_id) == scc_found.end()) {
|
||||
std::vector<node_t> que;
|
||||
que.emplace_back(source_id);
|
||||
while (!que.empty()) {
|
||||
node_t v_id = que.back();
|
||||
if (preorder.find(v_id) == preorder.end()) {
|
||||
i += 1;
|
||||
preorder[v_id] = i;
|
||||
}
|
||||
bool done = true;
|
||||
adj_attr_dict_factory& v_neighbors = adj[v_id];
|
||||
for (adj_attr_dict_factory::iterator w = v_neighbors.begin(); w != v_neighbors.end(); w++) {
|
||||
node_t w_id = w->first;
|
||||
if (preorder.find(w_id) == preorder.end()) {
|
||||
que.emplace_back(w_id);
|
||||
done = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (done) {
|
||||
lowlink[v_id] = preorder[v_id];
|
||||
for (adj_attr_dict_factory::iterator w = v_neighbors.begin(); w != v_neighbors.end(); w++) {
|
||||
node_t w_id = w->first;
|
||||
if (scc_found.find(w_id) == scc_found.end()) {
|
||||
if (preorder[w_id] > preorder[v_id]) {
|
||||
lowlink[v_id] = std::min(lowlink[v_id], lowlink[w_id]);
|
||||
} else {
|
||||
lowlink[v_id] = std::min(lowlink[v_id], preorder[w_id]);
|
||||
}
|
||||
}
|
||||
}
|
||||
que.pop_back();
|
||||
if (lowlink[v_id] == preorder[v_id]) {
|
||||
std::unordered_set<node_t> scc;
|
||||
scc.emplace(v_id);
|
||||
while (!scc_queue.empty() && (preorder[scc_queue.back()] > preorder[v_id])) {
|
||||
node_t k = scc_queue.back();
|
||||
scc_queue.pop_back();
|
||||
scc.emplace(k);
|
||||
}
|
||||
|
||||
py::set tmp_res;
|
||||
for (std::unordered_set<node_t>::iterator z = scc.begin(); z != scc.end(); z++) {
|
||||
scc_found.emplace(*z);
|
||||
tmp_res.add(G_.id_to_node.attr("get")(*z));
|
||||
}
|
||||
res.append(tmp_res);
|
||||
} else {
|
||||
scc_queue.emplace_back(v_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../common/common.h"
|
||||
|
||||
py::object strongly_connected_components(py::object G);
|
||||
py::object strongly_connected_components_iteration_impl(py::object G);
|
||||
Reference in New Issue
Block a user