#include #include #include #include #include #include #include #include #include #include #include "../../classes/graph.h" #include "../../common/utils.h" #include "../../classes/linkgraph.h" #include "localsearch.h" namespace py = pybind11; using namespace std; struct RootDecision { int superior; int path_length; int degree; }; int choose_center(const vector>& sorted_multi) { if (sorted_multi.size() < 2) { return 1; } vector y; for (const auto& p : sorted_multi) { y.push_back(p.second); } vector delta; for (size_t i = 1; i < y.size(); ++i) { delta.push_back(fabs(y[i] - y[i-1])); } if (delta.empty()) { return 1; } vector delta_nozero; for (double d : delta) { if (d != 0) { delta_nozero.push_back(d); } } if (delta_nozero.empty()) { return 1; } double mean = 0; for (double d : delta_nozero) { mean += d; } mean /= delta_nozero.size(); double variance = 0; for (double d : delta_nozero) { variance += (d - mean) * (d - mean); } variance /= delta_nozero.size(); double std_dev = sqrt(variance); double threshold = std_dev + mean; for (size_t i = 0; i < delta.size(); ++i) { if (delta[i] > threshold) { return i + 1; } } return 0; } py::object cpp_localsearch( py::object G, py::object center_num, py::object auto_choose_centers, py::object maximum_tree, py::object seed, py::object self_loop ) { Graph_generate_linkgraph(G, py::str("weight")); Graph& G_ = G.cast(); Graph_L GL = G_._get_linkgraph_structure(); py::dict id_to_node = G_.id_to_node; int n = GL.n; if (n == 0) { return py::make_tuple(py::none(), py::list(), py::list(), py::dict(), py::dict(), py::none()); } bool has_edges = false; for (int u = 1; u <= n; ++u) { if (GL.head[u] != -1) { has_edges = true; break; } } if (!has_edges) { py::dict result_grouped; py::list result_center_dcd; py::list result_y_dcd; py::dict result_y_partition; for (int u = 1; u <= n; ++u) { py::object node = id_to_node[py::cast(u)]; py::list members; members.append(node); result_grouped[node] = members; result_center_dcd.append(node); result_y_dcd.append(node); result_y_partition[node] = node; } return py::make_tuple(py::none(), result_center_dcd, result_y_dcd, result_y_partition, result_grouped, py::none()); } std::mt19937 rng(42); if (!seed.is_none()) { try { int seed_value = seed.cast(); rng.seed(seed_value); } catch (...) { } } std::uniform_real_distribution random_dist(0.0, 1.0); unordered_set selfloop_nodes; bool has_selfloop = self_loop.cast(); if (!has_selfloop) { selfloop_nodes.clear(); } vector degree(n + 1, 0); for (int u = 1; u <= n; ++u) { int deg = 0; for (int e = GL.head[u]; e != -1; e = GL.edges[e].next) { deg++; } if (selfloop_nodes.count(u)) { degree[u] = deg + 1; } else { degree[u] = deg; } } unordered_map> dag_adj; unordered_map> dag_pred; for (int v = 1; v <= n; ++v) { if (degree[v] == 0) continue; int kv = degree[v]; vector> neighbors; for (int e = GL.head[v]; e != -1; e = GL.edges[e].next) { int nn = GL.edges[e].to; if (nn == v && selfloop_nodes.count(v)) continue; int deg_nn = degree[nn]; neighbors.push_back({nn, deg_nn}); } if (!neighbors.empty()) { int knnmax = -1; for (auto& p : neighbors) { if (p.second > knnmax) knnmax = p.second; } if (knnmax >= kv) { for (auto& p : neighbors) { if (p.second == knnmax) { int nn = p.first; bool already_has = false; bool has_reverse = false; for (int existing : dag_adj[v]) { if (existing == nn) { already_has = true; break; } } for (int existing : dag_adj[nn]) { if (existing == v) { has_reverse = true; break; } } if (!already_has && !has_reverse) { dag_adj[v].push_back(nn); dag_pred[nn].push_back(v); } } } } } } vector out_degree_dag(n + 1, 0); for (int u = 1; u <= n; ++u) { out_degree_dag[u] = (int)dag_adj[u].size(); } vector roots; for (int u = 1; u <= n; ++u) { if (out_degree_dag[u] == 0 && degree[u] > 0) { roots.push_back(u); } } if (roots.empty()) { for (int u = 1; u <= n; ++u) { if (degree[u] > 0) { roots.push_back(u); } } } if (roots.size() > 1) { bool all_same_degree = true; int first_degree = -1; for (int root : roots) { if (first_degree == -1) { first_degree = degree[root]; } else if (degree[root] != first_degree) { all_same_degree = false; break; } } if (all_same_degree) { int max_root = -1; for (int root : roots) { if (root > max_root) { max_root = root; } } roots.clear(); roots.push_back(max_root); } } unordered_map tree_rootnode; unordered_map tree_parentnode; unordered_map tree_distancetoroot; for (int i = 1; i <= n; ++i) { tree_rootnode[i] = -1; tree_parentnode[i] = -1; tree_distancetoroot[i] = -1; } queue> bfs_queue; for (int root : roots) { bfs_queue.push({root, 0}); tree_rootnode[root] = root; tree_parentnode[root] = -1; tree_distancetoroot[root] = 0; } vector visited(n + 1, 0); for (int root : roots) { visited[root] = 1; } while (!bfs_queue.empty()) { int parent, dist; std::tie(parent, dist) = bfs_queue.front(); bfs_queue.pop(); for (int pred : dag_pred[parent]) { if (tree_distancetoroot[pred] != -1 && tree_distancetoroot[pred] < dist + 1) { continue; } if (tree_distancetoroot[pred] == -1) { tree_rootnode[pred] = tree_rootnode[parent]; tree_parentnode[pred] = parent; tree_distancetoroot[pred] = dist + 1; bfs_queue.push({pred, dist + 1}); } else if (tree_distancetoroot[pred] == dist + 1) { if (random_dist(rng) < 0.5) { continue; } tree_rootnode[pred] = tree_rootnode[parent]; tree_parentnode[pred] = parent; } } } unordered_map> root_to_node; for (int node = 1; node <= n; ++node) { int root = tree_rootnode[node]; if (root != -1) { root_to_node[root].push_back(node); } } vector valid_roots; for (auto& kv : root_to_node) { if ((int)kv.second.size() > 1) { valid_roots.push_back(kv.first); } } unordered_set root_set(valid_roots.begin(), valid_roots.end()); unordered_map root_decision; auto BFS_from_s = [&](int s) -> pair { queue search_queue; unordered_map path_dict; unordered_set seen; search_queue.push(s); seen.insert(s); path_dict[s] = 0; while (!search_queue.empty()) { int vertex = search_queue.front(); search_queue.pop(); int current_dist = path_dict[vertex]; vector> neighbors; for (int e = GL.head[vertex]; e != -1; e = GL.edges[e].next) { int nn = GL.edges[e].to; int deg_nn = degree[nn]; neighbors.push_back({nn, deg_nn}); } sort(neighbors.begin(), neighbors.end(), [](const pair& a, const pair& b) { return a.second > b.second; }); for (auto& p : neighbors) { int w = p.first; if (!seen.count(w)) { path_dict[w] = current_dist + 1; seen.insert(w); search_queue.push(w); } if (root_set.count(w) && degree[w] > degree[s]) { return {w, path_dict[w]}; } } } return {s, -1}; }; for (int root : valid_roots) { auto result = BFS_from_s(root); root_decision[root] = {result.first, result.second, degree[root]}; } int max_path = -1; for (auto& kv : root_decision) { if (kv.second.path_length > max_path) { max_path = kv.second.path_length; } } if (max_path < 0) max_path = 2; for (auto& kv : root_decision) { if (kv.second.path_length == -1) { kv.second.path_length = max_path; } } unordered_map node_plot = root_decision; for (int node = 1; node <= n; ++node) { if (node_plot.find(node) == node_plot.end() && degree[node] > 0) { int parent = tree_parentnode[node]; node_plot[node] = {parent, 1, degree[node]}; } } vector node_ids; vector degrees; vector path_lens; for (auto& kv : node_plot) { node_ids.push_back(kv.first); degrees.push_back(kv.second.degree); path_lens.push_back(kv.second.path_length); } for (size_t i = 0; i < path_lens.size(); ++i) { if (degrees[i] <= 1) { path_lens[i] = 1; } } unordered_map degree_rank; vector> sorted_by_deg; for (size_t i = 0; i < node_ids.size(); ++i) { sorted_by_deg.push_back({degrees[i], node_ids[i]}); } sort(sorted_by_deg.begin(), sorted_by_deg.end(), [](const pair& a, const pair& b) { return a.first < b.first; }); int rank = 1; int last_deg = -1; for (auto& p : sorted_by_deg) { if (p.first != last_deg) { degree_rank[p.second] = rank; rank++; last_deg = p.first; } else { degree_rank[p.second] = rank - 1; } } int min_rank = 1; int max_rank = rank - 1; double rank_range = (max_rank - min_rank); if (rank_range == 0) rank_range = 1; vector square_path(node_ids.size()); double max_sq_path = 0; double min_sq_path = 1e9; for (size_t i = 0; i < node_ids.size(); ++i) { square_path[i] = (double)path_lens[i] * (double)path_lens[i]; if (square_path[i] > max_sq_path) max_sq_path = square_path[i]; if (square_path[i] < min_sq_path) min_sq_path = square_path[i]; } double sq_range = max_sq_path - min_sq_path; if (sq_range == 0) sq_range = 1; unordered_map multi_dict; for (size_t i = 0; i < node_ids.size(); ++i) { int node = node_ids[i]; double norm_deg, norm_sq_path; if (max_rank == min_rank) { norm_deg = 1.0 / (double)node_ids.size(); } else { norm_deg = (double)(degree_rank[node] - min_rank) / rank_range; } if (max_sq_path == min_sq_path) { norm_sq_path = 1.0 / (double)node_ids.size(); } else { norm_sq_path = (square_path[i] - min_sq_path) / sq_range; } multi_dict[node] = norm_deg * norm_sq_path; } vector> sorted_multi; for (auto& kv : multi_dict) { sorted_multi.push_back(kv); } sort(sorted_multi.begin(), sorted_multi.end(), [](const pair& a, const pair& b) { if (fabs(a.second - b.second) > 1e-9) return a.second > b.second; return a.first > b.first; }); int num_centers = (int)valid_roots.size(); bool auto_choose = auto_choose_centers.cast(); if (auto_choose && sorted_multi.size() > 0) { int auto_centernum = choose_center(sorted_multi); if (!center_num.is_none()) { int user_center_num = center_num.cast(); num_centers = (auto_centernum < user_center_num) ? auto_centernum : user_center_num; } else { num_centers = auto_centernum; } } else if (!center_num.is_none()) { num_centers = center_num.cast(); } if (num_centers <= 0) { num_centers = (int)valid_roots.size(); } vector center_dcd; int local_cnt = 0; for (size_t i = 0; i < sorted_multi.size() && local_cnt < num_centers; ++i) { if (sorted_multi[i].second > 0) { local_cnt++; center_dcd.push_back(sorted_multi[i].first); } } if (center_dcd.empty() && !sorted_multi.empty()) { center_dcd.push_back(sorted_multi[0].first); } bool all_same_degree = true; int first_deg = -1; for (int i = 1; i <= n && all_same_degree; ++i) { if (degree[i] > 0) { if (first_deg == -1) { first_deg = degree[i]; } else if (degree[i] != first_deg) { all_same_degree = false; } } } if (all_same_degree && n > 0) { center_dcd.clear(); center_dcd.push_back(n); } unordered_set center_set(center_dcd.begin(), center_dcd.end()); for (int node : valid_roots) { int superior = root_decision[node].superior; tree_parentnode[node] = superior; tree_rootnode[node] = superior; } for (int node = 0; node < n; ++node) { if (degree[node] > 0 && center_set.count(node)) { tree_rootnode[node] = node; } } for (int node = 0; node < n; ++node) { if (degree[node] == 0) continue; vector recent; recent.push_back(node); bool flag = false; while (center_set.find(tree_rootnode[node]) == center_set.end() && !flag) { int j = tree_rootnode[node]; if (j == -1 || find(recent.begin(), recent.end(), j) != recent.end()) { tree_rootnode[node] = -1; flag = true; break; } recent.push_back(j); tree_rootnode[node] = tree_rootnode[j]; } } unordered_map y_partition; vector y_dcd; for (int node = 1; node <= n; ++node) { if (degree[node] == 0) continue; int root = tree_rootnode[node]; if (root == -1 && !center_dcd.empty()) { root = center_dcd[0]; tree_rootnode[node] = root; } y_partition[node] = root; if (root == -1) { y_dcd.push_back(-1); } else { y_dcd.push_back(root); } } unordered_map> grouped; for (auto& kv : y_partition) { int center = kv.second; if (center != -1) { grouped[center].push_back(kv.first); } } py::dict result_grouped; for (auto& kv : grouped) { py::list members; for (int node_id : kv.second) { members.append(id_to_node[py::cast(node_id)]); } result_grouped[id_to_node[py::cast(kv.first)]] = members; } py::list result_center_dcd; for (int center : center_dcd) { result_center_dcd.append(id_to_node[py::cast(center)]); } py::list result_y_dcd; for (int label : y_dcd) { if (label == -1) { result_y_dcd.append(py::cast(-1)); } else { result_y_dcd.append(id_to_node[py::cast(label)]); } } py::dict result_y_partition; for (auto& kv : y_partition) { if (kv.second == -1) { result_y_partition[id_to_node[py::cast(kv.first)]] = py::cast(-1); } else { result_y_partition[id_to_node[py::cast(kv.first)]] = id_to_node[py::cast(kv.second)]; } } return py::make_tuple( py::none(), result_center_dcd, result_y_dcd, result_y_partition, result_grouped, py::none() ); }