first commit
This commit is contained in:
58
.clang-format
Normal file
58
.clang-format
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
# Generated from CLion C/C++ Code Style settings
|
||||||
|
---
|
||||||
|
Language: Cpp
|
||||||
|
BasedOnStyle: LLVM
|
||||||
|
AccessModifierOffset: -4
|
||||||
|
AlignConsecutiveAssignments: false
|
||||||
|
AlignConsecutiveDeclarations: false
|
||||||
|
AlignOperands: true
|
||||||
|
AlignTrailingComments: false
|
||||||
|
AlwaysBreakTemplateDeclarations: Yes
|
||||||
|
BraceWrapping:
|
||||||
|
AfterCaseLabel: false
|
||||||
|
AfterClass: false
|
||||||
|
AfterControlStatement: false
|
||||||
|
AfterEnum: false
|
||||||
|
AfterFunction: false
|
||||||
|
AfterNamespace: false
|
||||||
|
AfterStruct: false
|
||||||
|
AfterUnion: false
|
||||||
|
AfterExternBlock: false
|
||||||
|
BeforeCatch: false
|
||||||
|
BeforeElse: false
|
||||||
|
BeforeLambdaBody: false
|
||||||
|
BeforeWhile: false
|
||||||
|
SplitEmptyFunction: true
|
||||||
|
SplitEmptyRecord: true
|
||||||
|
SplitEmptyNamespace: true
|
||||||
|
BreakBeforeBraces: Custom
|
||||||
|
BreakConstructorInitializers: AfterColon
|
||||||
|
BreakConstructorInitializersBeforeComma: false
|
||||||
|
ColumnLimit: 120
|
||||||
|
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
||||||
|
ContinuationIndentWidth: 8
|
||||||
|
IncludeCategories:
|
||||||
|
- Regex: '^<.*'
|
||||||
|
Priority: 1
|
||||||
|
- Regex: '^".*'
|
||||||
|
Priority: 2
|
||||||
|
- Regex: '.*'
|
||||||
|
Priority: 3
|
||||||
|
IncludeIsMainRegex: '([-_](test|unittest))?$'
|
||||||
|
IndentCaseLabels: true
|
||||||
|
IndentWidth: 4
|
||||||
|
InsertNewlineAtEOF: true
|
||||||
|
MacroBlockBegin: ''
|
||||||
|
MacroBlockEnd: ''
|
||||||
|
MaxEmptyLinesToKeep: 2
|
||||||
|
NamespaceIndentation: All
|
||||||
|
SpaceAfterCStyleCast: true
|
||||||
|
SpaceAfterTemplateKeyword: false
|
||||||
|
SpaceBeforeRangeBasedForLoopColon: false
|
||||||
|
SpaceInEmptyParentheses: false
|
||||||
|
SpacesInAngles: false
|
||||||
|
SpacesInConditionalStatement: false
|
||||||
|
SpacesInCStyleCastParentheses: false
|
||||||
|
SpacesInParentheses: false
|
||||||
|
TabWidth: 4
|
||||||
|
...
|
||||||
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
.idea
|
||||||
|
.vscode
|
||||||
|
.cache
|
||||||
|
build
|
||||||
|
|
||||||
8
CMakeLists.txt
Normal file
8
CMakeLists.txt
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
cmake_minimum_required(VERSION 4.0)
|
||||||
|
project(HG)
|
||||||
|
|
||||||
|
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||||
|
set(CMAKE_CXX_STANDARD 26)
|
||||||
|
|
||||||
|
add_executable(HyperGraph main.cpp
|
||||||
|
HyperGraph.hpp)
|
||||||
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
|
||||||
31
main.cpp
Normal file
31
main.cpp
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include "HyperGraph.hpp"
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
HyperGraph g;
|
||||||
|
|
||||||
|
// v0~v7
|
||||||
|
for (int i{0}; i <= 7; ++i) {
|
||||||
|
g.add_vertex(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// e0: v1, v2
|
||||||
|
g.add_edge({1, 2}, 0);
|
||||||
|
// e1: v2, v6
|
||||||
|
g.add_edge({2, 6}, 1);
|
||||||
|
// e2: v0, v2, v4, v7
|
||||||
|
g.add_edge({0, 2, 4, 7}, 2);
|
||||||
|
// e3: v0, v3, v5, v7
|
||||||
|
g.add_edge({0, 3, 5, 7}, 3);
|
||||||
|
auto res = g.PR();
|
||||||
|
double total = 0;
|
||||||
|
for (const auto &val: res) {
|
||||||
|
total += val;
|
||||||
|
std::cout << val << " ";
|
||||||
|
}
|
||||||
|
std::cout << "\nTotal: " << total << std::endl;
|
||||||
|
|
||||||
|
auto chg = CompressedHyperGraph<int, int>::from_hypergraph(g);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user