From 17ba627f72fe6c86f4121d6595935b2b038effb5 Mon Sep 17 00:00:00 2001 From: Jeffrey Hsu Date: Sun, 2 Nov 2025 21:46:02 +0800 Subject: [PATCH] first commit --- .clang-format | 58 +++++++++++ .gitignore | 5 + CMakeLists.txt | 8 ++ HyperGraph.hpp | 256 +++++++++++++++++++++++++++++++++++++++++++++++++ main.cpp | 31 ++++++ 5 files changed, 358 insertions(+) create mode 100644 .clang-format create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 HyperGraph.hpp create mode 100644 main.cpp diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..a8207b4 --- /dev/null +++ b/.clang-format @@ -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 +... diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2babb0d --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.idea +.vscode +.cache +build + diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..477c188 --- /dev/null +++ b/CMakeLists.txt @@ -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) diff --git a/HyperGraph.hpp b/HyperGraph.hpp new file mode 100644 index 0000000..9cc780c --- /dev/null +++ b/HyperGraph.hpp @@ -0,0 +1,256 @@ +// +// 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 diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..3840f38 --- /dev/null +++ b/main.cpp @@ -0,0 +1,31 @@ +#include +#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::from_hypergraph(g); + + return 0; +}