// // Created by plana on 2025/10/29. // #ifndef HYPERGRAPH_HYPERGRAPH_H #define HYPERGRAPH_HYPERGRAPH_H #include #include #include #include using namespace std; using EdgeId = size_t; // 超边ID类型 using VertexId = size_t; // 顶点ID类型 template class AbstractHyperGraph { protected: vector edge_data; // 超边数据 vector vertex_data; // 顶点数据 public: virtual ~AbstractHyperGraph() = default; virtual VertexId add_vertex(const VData &data = VData()) = 0; virtual EdgeId add_edge(const vector &vertices, const EData &data = EData()) = 0; virtual vector PR(double alpha = 0.85, double tol = 1e-8, size_t max_iter = 100) const = 0; }; template class CompressedHyperGraph; template class HyperGraph final : public AbstractHyperGraph { friend CompressedHyperGraph; private: vector> edge_to_vertex; // 超边到顶点的映射 vector> vertex_to_edge; // 顶点到超边的映射 public: explicit HyperGraph() = default; ~HyperGraph() override = default; /* * 添加超边 * 参数:data - 超边数据 */ VertexId add_vertex(const VData &data = VData()) override { this->vertex_data.emplace_back(data); vertex_to_edge.emplace_back(); return vertex_to_edge.size() - 1; } /* * 添加超边 * 参数:vertices - 连接的顶点ID列表 * data - 超边数据 */ EdgeId add_edge(const vector &vertices, const EData &data = EData()) override { this->edge_data.emplace_back(data); edge_to_vertex.emplace_back(); EdgeId eid = edge_to_vertex.size() - 1; for (const auto &vid: vertices) { // 检查顶点ID的有效性 if (vid >= vertex_to_edge.size()) { throw out_of_range("Invalid vertex ID"); } // 建立超边与顶点的双向映射 edge_to_vertex[eid].emplace(vid); vertex_to_edge[vid].emplace(eid); } return eid; } vector PR(double alpha = 0.85, double tol = 1e-8, size_t max_iter = 100) const override { return PageRank(*this, alpha, tol, max_iter); } static vector PageRank(const HyperGraph &g, double alpha, double tol, size_t max_iter) { size_t n = g.vertex_data.size(); vector rank(n, 0.0); if (n == 0) return rank; // 初始化为均匀分布 double inv_n = 1.0 / static_cast(n); for (size_t i = 0; i < n; ++i) rank[i] = inv_n; vector next_rank(n, 0.0); for (size_t iter = 0; iter < max_iter; ++iter) { ranges::fill(next_rank, 0.0); double dangling_sum = 0.0; // 对每个顶点分发其 PageRank for (size_t u = 0; u < n; ++u) { double pu = rank[u]; const auto &inc_edges = g.vertex_to_edge[u]; size_t deg_u = inc_edges.size(); if (deg_u == 0) { // dangling 节点,累计其 PageRank dangling_sum += pu; continue; } double share_per_edge = pu / static_cast(deg_u); for (EdgeId e: inc_edges) { const auto &verts = g.edge_to_vertex[e]; size_t size_e = verts.size(); if (size_e == 0) continue; // 防御性检查 double per_v = share_per_edge / static_cast(size_e); for (VertexId v: verts) { next_rank[v] += per_v; } } } // 将 dangling mass 均匀分配并应用阻尼因子与随机跳转 double dangling_contrib = dangling_sum * inv_n; double teleport = (1.0 - alpha) * inv_n; double scale = alpha; double diff = 0.0; for (size_t v = 0; v < n; ++v) { double new_r = scale * (next_rank[v] + dangling_contrib) + teleport; diff += fabs(new_r - rank[v]); next_rank[v] = new_r; } rank.swap(next_rank); if (diff < tol) break; } return rank; } }; template class CompressedHyperGraph final : public AbstractHyperGraph { struct IDRange { size_t start; size_t end; }; template struct CompressedData { vector dataSet; vector dataID; }; private: vector edge_set; vector vertex_set; vector edge_ranges; vector vertex_ranges; public: CompressedHyperGraph() = default; VertexId add_vertex(const VData &data) override {} EdgeId add_edge(const vector &vertices, const EData &data) override {} vector PR(double alpha, double tol, size_t max_iter) const override {} static CompressedHyperGraph from_hypergraph(const HyperGraph &g) { auto compress_func = [](vector> vec) { size_t s{vec.size() - 1}; // m or n vector dataSet; // eSet or vSet unordered_set dataX; // e* or v* unordered_set dataJ; // e- or v- vector dataID(s + 1); // eID or vID size_t cnt{0}; for (int i{0}; i <= s; ++i) { auto &dataI = vec[i]; // ei or vi if (i == s) { for (const VertexId u: difference(dataI, dataJ)) { dataSet.emplace_back(u); ++cnt; } dataID[i].end = cnt; } else { auto &dataIp1 = vec[i + 1]; // ei+1 or vi+1 dataX = convertFromVector(intersection(dataI, dataIp1)); for (const VertexId u: difference(dataI, convertFromVector(union_set(dataX, dataJ)))) { dataSet.emplace_back(u); ++cnt; } dataID[i + 1].start = cnt; for (const VertexId u: difference(dataX, dataJ)) { dataSet.emplace_back(u); ++cnt; } dataID[i].end = cnt; dataJ = dataX; } } return CompressedData{dataSet, dataID}; }; auto [eSet, eID] = compress_func(g.edge_to_vertex); auto [vSet, vID] = compress_func(g.vertex_to_edge); return {}; } private: template static vector difference(const unordered_set &a, const unordered_set &b) { vector res{}; for (const auto &val: a) { if (!b.contains(val)) { res.emplace_back(val); } } return res; } template static vector intersection(const unordered_set &a, const unordered_set &b) { vector res{}; for (const auto &val: a) { if (b.contains(val)) { res.emplace_back(val); } } return res; } template static vector union_set(const unordered_set &a, const unordered_set &b) { unordered_set temp = a; for (const auto &val: b) { temp.insert(val); } return vector(temp.begin(), temp.end()); } template static unordered_set convertFromVector(const vector &vec) { return unordered_set(vec.begin(), vec.end()); } }; #endif // HYPERGRAPH_HYPERGRAPH_H