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

298 lines
9.5 KiB
C++

#include "cluster.h"
#include "../../classes/graph.h"
#include "../../classes/directed_graph.h"
#include "../../common/utils.h"
inline weight_t wt(adj_dict_factory& adj, node_t u, node_t v, std::string weight, weight_t max_weight = 1) {
auto& attr = adj[u][v];
return (attr.count(weight) ? attr[weight] : 1) / max_weight;
}
py::list _weighted_triangles_and_degree(py::object G, py::object nodes, py::object weight) {
std::string weight_key = weight_to_string(weight);
Graph& G_ = G.cast<Graph&>();
auto& adj = G_.adj;
weight_t max_weight = 1;
if (weight.is_none() || G.attr("number_of_edges")().equal(py::cast(0))) {
max_weight = 1;
}
else {
int assigned = 0;
for (auto& u_info : G_.adj) {
for (auto& v_info : u_info.second) {
auto& d = v_info.second;
if (assigned) {
max_weight = std::max(max_weight, d.count(weight_key) ? d[weight_key] : 1);
}
else {
assigned = 1;
max_weight = d.count(weight_key) ? d[weight_key] : 1;
}
}
}
}
py::list nodes_list = py::list(G.attr("nbunch_iter")(nodes));
py::list ret = py::list();
for (int i = 0;i < py::len(nodes_list);i++) {
node_t i_id = (G_.node_to_id[nodes_list[i]]).cast<node_t>();
std::unordered_set<node_t> inbrs, seen;
for (const auto& pair : adj[i_id]) {
inbrs.insert(pair.first);
}
inbrs.erase(i_id);
weight_t weighted_triangles = 0;
for (const auto& j_id : inbrs) {
seen.insert(j_id);
weight_t wij = wt(adj, i_id, j_id, weight_key, max_weight);
for (const auto& k_id : inbrs) {
if (adj[j_id].count(k_id) && !seen.count(k_id)) {
weight_t wjk = wt(adj, j_id, k_id, weight_key, max_weight);
weight_t wki = wt(adj, k_id, i_id, weight_key, max_weight);
weighted_triangles += std::cbrt(wij * wjk * wki);
}
}
}
ret.append(py::make_tuple(G_.id_to_node[py::cast(i_id)], inbrs.size(), 2 * weighted_triangles));
}
return ret;
}
py::list _directed_weighted_triangles_and_degree(py::object G, py::object nodes, py::object weight) {
std::string weight_key = weight_to_string(weight);
DiGraph& G_ = G.cast<DiGraph&>();
auto& adj = G_.adj;
weight_t max_weight = 1;
if (weight.is_none() || G.attr("number_of_edges")().equal(py::cast(0))) {
max_weight = 1;
}
else {
int assigned = 0;
for (auto& u_info : G_.adj) {
for (auto& v_info : u_info.second) {
auto& d = v_info.second;
if (assigned) {
max_weight = std::max(max_weight, d.count(weight_key) ? d[weight_key] : 1);
}
else {
assigned = 1;
max_weight = d.count(weight_key) ? d[weight_key] : 1;
}
}
}
}
py::list nodes_list = py::list(G.attr("nbunch_iter")(nodes));
py::list ret = py::list();
for (int i = 0;i < py::len(nodes_list);i++) {
node_t i_id = (G_.node_to_id[nodes_list[i]]).cast<node_t>();
std::unordered_set<node_t> ipreds, isuccs;
for (const auto& pair : G_.pred[i_id]) {
ipreds.insert(pair.first);
}
ipreds.erase(i_id);
for (const auto& pair : G_.adj[i_id]) {
isuccs.insert(pair.first);
}
isuccs.erase(i_id);
weight_t directed_triangles = 0;
for (const auto& j_id : ipreds) {
for (const auto& k_pair : G_.pred[j_id]) {
node_t k_id = k_pair.first;
if (k_id == j_id) {
continue;
}// jpreds
if (ipreds.count(k_id)) { // ipreds & jpreds
directed_triangles += std::cbrt(wt(adj, j_id, i_id, weight_key, max_weight) * wt(adj, k_id, i_id, weight_key, max_weight) * wt(adj, k_id, j_id, weight_key, max_weight));
}
if (isuccs.count(k_id)) { // isuccs & jpreds
directed_triangles += std::cbrt(wt(adj, j_id, i_id, weight_key, max_weight) * wt(adj, i_id, k_id, weight_key, max_weight) * wt(adj, k_id, j_id, weight_key, max_weight));
}
}
for (const auto& k_pair : G_.adj[j_id]) {
node_t k_id = k_pair.first;
if (k_id == j_id) {
continue;
}// jsuccs
if (ipreds.count(k_id)) { // ipreds & jsuccs
directed_triangles += std::cbrt(wt(adj, j_id, i_id, weight_key, max_weight) * wt(adj, k_id, i_id, weight_key, max_weight) * wt(adj, j_id, k_id, weight_key, max_weight));
}
if (isuccs.count(k_id)) { // isuccs & jsuccs
directed_triangles += std::cbrt(wt(adj, j_id, i_id, weight_key, max_weight) * wt(adj, i_id, k_id, weight_key, max_weight) * wt(adj, j_id, k_id, weight_key, max_weight));
}
}
}
for (const auto& j_id : isuccs) {
for (const auto& k_pair : G_.pred[j_id]) {
node_t k_id = k_pair.first;
if (k_id == j_id) {
continue;
}// jpreds
if (ipreds.count(k_id)) { // ipreds & jpreds
directed_triangles += std::cbrt(wt(adj, i_id, j_id, weight_key, max_weight) * wt(adj, k_id, i_id, weight_key, max_weight) * wt(adj, k_id, j_id, weight_key, max_weight));
}
if (isuccs.count(k_id)) { // isuccs & jpreds
directed_triangles += std::cbrt(wt(adj, i_id, j_id, weight_key, max_weight) * wt(adj, i_id, k_id, weight_key, max_weight) * wt(adj, k_id, j_id, weight_key, max_weight));
}
}
for (const auto& k_pair : G_.adj[j_id]) {
node_t k_id = k_pair.first;
if (k_id == j_id) {
continue;
}// jsuccs
if (ipreds.count(k_id)) { // ipreds & jsuccs
directed_triangles += std::cbrt(wt(adj, i_id, j_id, weight_key, max_weight) * wt(adj, k_id, i_id, weight_key, max_weight) * wt(adj, j_id, k_id, weight_key, max_weight));
}
if (isuccs.count(k_id)) { // isuccs & jsuccs
directed_triangles += std::cbrt(wt(adj, i_id, j_id, weight_key, max_weight) * wt(adj, i_id, k_id, weight_key, max_weight) * wt(adj, j_id, k_id, weight_key, max_weight));
}
}
}
int dtotal = ipreds.size() + isuccs.size();
int dbidirectional = 0;
for (const auto& node : ipreds) {
dbidirectional += isuccs.count(node);
}
ret.append(py::make_tuple(nodes_list[i], dtotal, dbidirectional, directed_triangles));
}
return ret;
}
py::list _triangles_and_degree(py::object G, py::object nodes = py::none()) {
Graph& G_ = G.cast<Graph&>();
auto& adj = G_.adj;
py::list nodes_list = py::list(G.attr("nbunch_iter")(nodes));
py::list ret = py::list();
for (int i = 0;i < py::len(nodes_list);i++) {
node_t v = (G_.node_to_id[nodes_list[i]]).cast<node_t>();
std::unordered_set<node_t> vs;
for (const auto& pair : adj[v]) {
vs.insert(pair.first);
}
vs.erase(v);
weight_t ntriangles = 0;
for (const auto& w : vs) {
for (const auto& node : vs) {
ntriangles += node != w && adj[w].count(node);
}
}
ret.append(py::make_tuple(G_.id_to_node[py::cast(v)], vs.size(), ntriangles));
}
return ret;
}
py::list _directed_triangles_and_degree(py::object G, py::object nodes = py::none()) {
DiGraph& G_ = G.cast<DiGraph&>();
auto& adj = G_.adj;
py::list nodes_list = py::list(G.attr("nbunch_iter")(nodes));
py::list ret = py::list();
for (int i = 0;i < py::len(nodes_list);i++) {
node_t i_id = (G_.node_to_id[nodes_list[i]]).cast<node_t>();
std::unordered_set<node_t> ipreds, isuccs;
for (const auto& pair : G_.pred[i_id]) {
ipreds.insert(pair.first);
}
ipreds.erase(i_id);
for (const auto& pair : G_.adj[i_id]) {
isuccs.insert(pair.first);
}
isuccs.erase(i_id);
weight_t directed_triangles = 0;
for (const auto& j_id : ipreds) {
for (const auto& k_pair : G_.pred[j_id]) {
node_t k_id = k_pair.first;
if (k_id == j_id) {
continue;
}// jpreds
directed_triangles += ipreds.count(k_id); // ipreds & jpreds
directed_triangles += isuccs.count(k_id); // isuccs & jpreds
}
for (const auto& k_pair : G_.adj[j_id]) {
node_t k_id = k_pair.first;
if (k_id == j_id) {
continue;
}// jsuccs
directed_triangles += ipreds.count(k_id); // ipreds & jsuccs
directed_triangles += isuccs.count(k_id); // isuccs & jsuccs
}
}
for (const auto& j_id : isuccs) {
for (const auto& k_pair : G_.pred[j_id]) {
node_t k_id = k_pair.first;
if (k_id == j_id) {
continue;
}// jpreds
directed_triangles += ipreds.count(k_id); // ipreds & jpreds
directed_triangles += isuccs.count(k_id); // isuccs & jpreds
}
for (const auto& k_pair : G_.adj[j_id]) {
node_t k_id = k_pair.first;
if (k_id == j_id) {
continue;
}// jsuccs
directed_triangles += ipreds.count(k_id); // ipreds & jsuccs
directed_triangles += isuccs.count(k_id); // isuccs & jsuccs
}
}
int dtotal = ipreds.size() + isuccs.size();
int dbidirectional = 0;
for (const auto& node : ipreds) {
dbidirectional += isuccs.count(node);
}
ret.append(py::make_tuple(nodes_list[i], dtotal, dbidirectional, directed_triangles));
}
return ret;
}
py::object clustering(py::object G, py::object nodes, py::object weight) {
py::dict clusterc = py::dict();
if (G.attr("is_directed")().cast<bool>()) {
py::list td_list;
if (!weight.is_none()) {
td_list = _directed_weighted_triangles_and_degree(G, nodes, weight);
}
else {
td_list = _directed_triangles_and_degree(G, nodes);
}
for (int i = 0;i < py::len(td_list);i++) {
py::tuple tuple = td_list[i].cast<py::tuple>();
py::object v = tuple[0];
int dt = tuple[1].cast<int>(), db = tuple[2].cast<int>();
weight_t t = tuple[3].cast<weight_t>();
if (t == 0) {
clusterc[v] = 0;
}
else {
clusterc[v] = t / ((dt * (dt - 1) - 2 * db) * 2);
}
}
}
else {
py::list td_list;
if (!weight.is_none()) {
td_list = _weighted_triangles_and_degree(G, nodes, weight);
}
else {
td_list = _triangles_and_degree(G, nodes);
}
for (int i = 0;i < py::len(td_list);i++) {
py::tuple tuple = td_list[i].cast<py::tuple>();
py::object v = tuple[0];
int d = tuple[1].cast<int>();
weight_t t = tuple[2].cast<weight_t>();
if (t == 0) {
clusterc[v] = 0;
}
else {
clusterc[v] = t / (d * (d - 1));
}
}
}
if (G.contains(nodes)) {
return clusterc[nodes];
}
return clusterc;
}