#include #include #include #include #include #include #include #ifdef _OPENMP #include #endif #include "../../classes/graph.h" #include "../../common/utils.h" namespace py = pybind11; class CSRMatrix { public: std::vector indptr; std::vector indices; std::vector data; // Empty if unweighted (all 1.0) int rows, cols; bool is_weighted; CSRMatrix(int r, int c) : rows(r), cols(c), is_weighted(false) { indptr.assign(r + 1, 0); } }; // Power iteration with branch optimization for weighted/unweighted paths std::vector power_iteration_optimized( const CSRMatrix& A, int max_iter, double tol, std::vector& x ) { const int n = A.rows; std::vector x_next(n); bool use_weight = A.is_weighted && !A.data.empty(); // Initial normalization double norm = 0.0; #pragma omp parallel for reduction(+:norm) for (int i = 0; i < n; ++i) norm += x[i] * x[i]; norm = std::sqrt(norm); if (norm < 1e-12) { std::fill(x.begin(), x.end(), 1.0 / std::sqrt(n)); } else { double inv_norm = 1.0 / norm; #pragma omp parallel for for (int i = 0; i < n; ++i) x[i] *= inv_norm; } double delta = tol + 1.0; for (int iter = 0; iter < max_iter && delta >= tol; ++iter) { double next_norm_sq = 0.0; #pragma omp parallel for reduction(+:next_norm_sq) schedule(dynamic, 64) for (int i = 0; i < n; ++i) { double sum = 0.0; const int start = A.indptr[i]; const int end = A.indptr[i+1]; if (use_weight) { for (int j = start; j < end; ++j) { sum += A.data[j] * x[A.indices[j]]; } } else { for (int j = start; j < end; ++j) { sum += x[A.indices[j]]; } } x_next[i] = sum; next_norm_sq += sum * sum; } double next_norm = std::sqrt(next_norm_sq); if (next_norm < 1e-12) break; double inv_next_norm = 1.0 / next_norm; delta = 0.0; #pragma omp parallel for reduction(+:delta) schedule(static) for (int i = 0; i < n; ++i) { double val = x_next[i] * inv_next_norm; delta += std::abs(val - x[i]); x_next[i] = val; } x.swap(x_next); } return x; } // Build transpose CSR with fallback logic for missing weight keys CSRMatrix build_transpose_matrix_smart(Graph& graph, const std::vector& nodes, const std::string& weight_key) { std::shared_ptr csr_ptr = weight_key.empty() ? graph.gen_CSR() : graph.gen_CSR(weight_key); int n = static_cast(nodes.size()); CSRMatrix At(n, n); if (!csr_ptr) return At; const auto& src_indptr = csr_ptr->V; const auto& src_indices = csr_ptr->E; std::vector src_data; bool actually_weighted = false; // Detect if weighted calculation is required if (!weight_key.empty()) { auto it = csr_ptr->W_map.find(weight_key); if (it != csr_ptr->W_map.end() && it->second) { src_data = *(it->second); for (double w : src_data) { if (std::abs(w - 1.0) > 1e-9) { actually_weighted = true; break; } } } } At.is_weighted = actually_weighted; // Calculate row counts for transpose for (int x_idx : src_indices) { if (x_idx >= 0 && x_idx < n) At.indptr[x_idx + 1]++; } for (int i = 0; i < n; ++i) At.indptr[i + 1] += At.indptr[i]; At.indices.resize(src_indices.size()); if (actually_weighted) At.data.resize(src_indices.size()); std::vector cur_pos(At.indptr.begin(), At.indptr.end()); // Populate transpose CSR data for (int r = 0; r < n; ++r) { for (int p = src_indptr[r]; p < src_indptr[r+1]; ++p) { int c = src_indices[p]; if (c < 0 || c >= n) continue; int dest = cur_pos[c]++; At.indices[dest] = r; if (actually_weighted) At.data[dest] = src_data[p]; } } return At; } py::object cpp_eigenvector_centrality( py::object G, py::object py_max_iter, py::object py_tol, py::object py_nstart, py::object py_weight ) { try { Graph& graph = G.cast(); int max_iter = py_max_iter.cast(); double tol = py_tol.cast(); std::string weight_key = py_weight.is_none() ? "" : py_weight.cast(); if (graph.node.empty()) return py::dict(); std::vector nodes; for (auto& pair : graph.node) nodes.push_back(pair.first); int n = nodes.size(); CSRMatrix A_transpose = build_transpose_matrix_smart(graph, nodes, weight_key); // Initialize x vector (prefer degree-based or uniform) std::vector x(n, 1.0 / n); if (!py_nstart.is_none()) { py::dict nstart = py_nstart.cast(); for (int i = 0; i < n; i++) { py::object node_obj = graph.id_to_node[py::cast(nodes[i])]; if (nstart.contains(node_obj)) x[i] = nstart[node_obj].cast(); } } else { for (int i = 0; i < n; i++) { int degree = A_transpose.indptr[i+1] - A_transpose.indptr[i]; x[i] = (degree > 0) ? (double)degree : 1.0/n; } } std::vector res = power_iteration_optimized(A_transpose, max_iter, tol, x); py::dict result; for (int i = 0; i < n; i++) { py::object node_obj = graph.id_to_node[py::cast(nodes[i])]; result[node_obj] = res[i]; } return result; } catch (const std::exception& e) { throw std::runtime_error(std::string("C++ Eigenvector Error: ") + e.what()); } }