Files
HyperGraph/HyperGraph.hpp
2025-11-02 21:46:02 +08:00

257 lines
7.9 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//
// Created by plana on 2025/10/29.
//
#ifndef HYPERGRAPH_HYPERGRAPH_H
#define HYPERGRAPH_HYPERGRAPH_H
#include <cmath>
#include <iostream>
#include <unordered_set>
#include <vector>
using namespace std;
using EdgeId = size_t; // 超边ID类型
using VertexId = size_t; // 顶点ID类型
template<typename EData = int, typename VData = int>
class AbstractHyperGraph {
protected:
vector<EData> edge_data; // 超边数据
vector<VData> vertex_data; // 顶点数据
public:
virtual ~AbstractHyperGraph() = default;
virtual VertexId add_vertex(const VData &data = VData()) = 0;
virtual EdgeId add_edge(const vector<VertexId> &vertices, const EData &data = EData()) = 0;
virtual vector<double> PR(double alpha = 0.85, double tol = 1e-8, size_t max_iter = 100) const = 0;
};
template<class Tx, class Ty>
class CompressedHyperGraph;
template<typename EData = int, typename VData = int>
class HyperGraph final : public AbstractHyperGraph<EData, VData> {
friend CompressedHyperGraph<EData, VData>;
private:
vector<unordered_set<VertexId>> edge_to_vertex; // 超边到顶点的映射
vector<unordered_set<EdgeId>> 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<VertexId> &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<double> 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<double> PageRank(const HyperGraph &g, double alpha, double tol, size_t max_iter) {
size_t n = g.vertex_data.size();
vector<double> rank(n, 0.0);
if (n == 0)
return rank;
// 初始化为均匀分布
double inv_n = 1.0 / static_cast<double>(n);
for (size_t i = 0; i < n; ++i)
rank[i] = inv_n;
vector<double> 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<double>(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<double>(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<typename EData = int, typename VData = int>
class CompressedHyperGraph final : public AbstractHyperGraph<EData, VData> {
struct IDRange {
size_t start;
size_t end;
};
template<typename T>
struct CompressedData {
vector<T> dataSet;
vector<IDRange> dataID;
};
private:
vector<VertexId> edge_set;
vector<EdgeId> vertex_set;
vector<IDRange> edge_ranges;
vector<IDRange> vertex_ranges;
public:
CompressedHyperGraph() = default;
VertexId add_vertex(const VData &data) override {}
EdgeId add_edge(const vector<VertexId> &vertices, const EData &data) override {}
vector<double> PR(double alpha, double tol, size_t max_iter) const override {}
static CompressedHyperGraph from_hypergraph(const HyperGraph<EData, VData> &g) {
auto compress_func = []<typename T>(vector<unordered_set<T>> vec) {
size_t s{vec.size() - 1}; // m or n
vector<T> dataSet; // eSet or vSet
unordered_set<T> dataX; // e* or v*
unordered_set<T> dataJ; // e- or v-
vector<IDRange> 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<T>{dataSet, dataID};
};
auto [eSet, eID] = compress_func(g.edge_to_vertex);
auto [vSet, vID] = compress_func(g.vertex_to_edge);
return {};
}
private:
template<typename T>
static vector<T> difference(const unordered_set<T> &a, const unordered_set<T> &b) {
vector<T> res{};
for (const auto &val: a) {
if (!b.contains(val)) {
res.emplace_back(val);
}
}
return res;
}
template<typename T>
static vector<T> intersection(const unordered_set<T> &a, const unordered_set<T> &b) {
vector<T> res{};
for (const auto &val: a) {
if (b.contains(val)) {
res.emplace_back(val);
}
}
return res;
}
template<typename T>
static vector<T> union_set(const unordered_set<T> &a, const unordered_set<T> &b) {
unordered_set<T> temp = a;
for (const auto &val: b) {
temp.insert(val);
}
return vector<T>(temp.begin(), temp.end());
}
template<typename T>
static unordered_set<T> convertFromVector(const vector<T> &vec) {
return unordered_set<T>(vec.begin(), vec.end());
}
};
#endif // HYPERGRAPH_HYPERGRAPH_H