first commit
This commit is contained in:
256
HyperGraph.hpp
Normal file
256
HyperGraph.hpp
Normal file
@@ -0,0 +1,256 @@
|
||||
//
|
||||
// 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
|
||||
Reference in New Issue
Block a user