Files
easy-graph--easy-graph/cpp_easygraph/functions/community/localsearch.cpp
T
2026-07-13 12:36:30 +08:00

614 lines
18 KiB
C++

#include <vector>
#include <unordered_map>
#include <unordered_set>
#include <algorithm>
#include <queue>
#include <map>
#include <random>
#include <cmath>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#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<pair<int, double>>& sorted_multi) {
if (sorted_multi.size() < 2) {
return 1;
}
vector<double> y;
for (const auto& p : sorted_multi) {
y.push_back(p.second);
}
vector<double> 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<double> 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&>();
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<int>();
rng.seed(seed_value);
} catch (...) {
}
}
std::uniform_real_distribution<double> random_dist(0.0, 1.0);
unordered_set<int> selfloop_nodes;
bool has_selfloop = self_loop.cast<bool>();
if (!has_selfloop) {
selfloop_nodes.clear();
}
vector<int> 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<int, vector<int>> dag_adj;
unordered_map<int, vector<int>> dag_pred;
for (int v = 1; v <= n; ++v) {
if (degree[v] == 0) continue;
int kv = degree[v];
vector<pair<int, int>> 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<int> out_degree_dag(n + 1, 0);
for (int u = 1; u <= n; ++u) {
out_degree_dag[u] = (int)dag_adj[u].size();
}
vector<int> 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<int, int> tree_rootnode;
unordered_map<int, int> tree_parentnode;
unordered_map<int, int> tree_distancetoroot;
for (int i = 1; i <= n; ++i) {
tree_rootnode[i] = -1;
tree_parentnode[i] = -1;
tree_distancetoroot[i] = -1;
}
queue<pair<int, int>> bfs_queue;
for (int root : roots) {
bfs_queue.push({root, 0});
tree_rootnode[root] = root;
tree_parentnode[root] = -1;
tree_distancetoroot[root] = 0;
}
vector<int> 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<int, vector<int>> 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<int> valid_roots;
for (auto& kv : root_to_node) {
if ((int)kv.second.size() > 1) {
valid_roots.push_back(kv.first);
}
}
unordered_set<int> root_set(valid_roots.begin(), valid_roots.end());
unordered_map<int, RootDecision> root_decision;
auto BFS_from_s = [&](int s) -> pair<int, int> {
queue<int> search_queue;
unordered_map<int, int> path_dict;
unordered_set<int> 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<pair<int, int>> 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<int, int>& a, const pair<int, int>& 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<int, RootDecision> 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<int> node_ids;
vector<int> degrees;
vector<int> 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<int, int> degree_rank;
vector<pair<int, int>> 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<int, int>& a, const pair<int, int>& 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<double> 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<int, double> 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<pair<int, double>> sorted_multi;
for (auto& kv : multi_dict) {
sorted_multi.push_back(kv);
}
sort(sorted_multi.begin(), sorted_multi.end(),
[](const pair<int, double>& a, const pair<int, double>& 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<bool>();
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<int>();
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<int>();
}
if (num_centers <= 0) {
num_centers = (int)valid_roots.size();
}
vector<int> 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<int> 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<int> 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<int, int> y_partition;
vector<int> 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<int, vector<int>> 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()
);
}