first commit

This commit is contained in:
2025-11-02 21:46:02 +08:00
commit 17ba627f72
5 changed files with 358 additions and 0 deletions

256
HyperGraph.hpp Normal file
View 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