VeloGraphX
High-performance dynamic graph analytics in C++20
Loading...
Searching...
No Matches
triangles.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <algorithm>
4#include <cstdint>
5#include <stdexcept>
6
9
10namespace velographx {
11
12template <class Graph>
14 public:
15 explicit BasicIncrementalTriangleCount(Graph& graph) : graph_(graph) {
17 recompute();
18 }
19 BasicIncrementalTriangleCount(Graph& graph, std::uint64_t trusted_initial_count)
20 : graph_(graph), triangles_(trusted_initial_count) {
22 }
23
24 [[nodiscard]] std::uint64_t value() const noexcept { return triangles_; }
25
26 void apply(const UpdateBatch& batch) {
27 if (batch.empty()) return;
28 for (const auto& op : batch.updates) {
29 if (op.src == op.dst) continue;
30 const bool exists = has_edge(graph_, op.src, op.dst);
31 const auto common = common_neighbors(op.src, op.dst);
32 if (op.add && !exists) triangles_ += common;
33 if (!op.add && exists) triangles_ -= std::min<std::uint64_t>(triangles_, common);
34 UpdateBatch one;
35 one.updates.push_back(op);
37 }
38 }
39
40 void recompute() {
41 std::uint64_t triple = 0;
42 for (VertexId u = 0; u < vertex_count(graph_); ++u) {
44 if (u < v) triple += common_neighbors(u, v);
45 });
46 }
47 triangles_ = triple / 3;
48 }
49
50 protected:
51 void validate_graph() const {
52 if (is_directed(graph_)) {
53 throw std::invalid_argument(
54 "IncrementalTriangleCount requires an undirected graph; directed motifs need an explicit definition");
55 }
56 }
57
58 [[nodiscard]] std::uint64_t common_neighbors(VertexId a, VertexId b) const {
59 VertexId scan = a;
60 VertexId probe = b;
61 if (neighbor_count(graph_, b) < neighbor_count(graph_, a)) std::swap(scan, probe);
62 std::uint64_t common = 0;
63 for_each_neighbor(graph_, scan, [&](VertexId v) {
64 if (has_edge(graph_, probe, v)) ++common;
65 });
66 return common;
67 }
68
69 Graph& graph_;
70 std::uint64_t triangles_{0};
71};
72
73// DynamicGraph forward-declares/friends this public specialization. Process
74// operations sequentially so later operations observe earlier updates, while a
75// logical UpdateBatch advances the graph version exactly once. Unlike the old
76// path, batch finalization also runs the graph's normal storage maintenance.
78 public:
81 IncrementalTriangleCount(DynamicGraph& graph, std::uint64_t trusted_initial_count)
82 : BasicIncrementalTriangleCount<DynamicGraph>(graph, trusted_initial_count) {}
83
84 void apply(const UpdateBatch& batch) {
85 if (batch.empty()) return;
86 for (const auto& op : batch.updates) {
87 if (op.src == op.dst) continue;
88 const bool exists = graph_.has_edge(op.src, op.dst);
89 const auto common = common_neighbors(op.src, op.dst);
90 if (op.add && !exists) triangles_ += common;
91 if (!op.add && exists) triangles_ -= std::min<std::uint64_t>(triangles_, common);
92 graph_.apply_unversioned(op);
93 }
94 ++graph_.version_;
95 graph_.automatic_storage_maintenance();
96 }
97};
98
99} // namespace velographx
BasicIncrementalTriangleCount(Graph &graph, std::uint64_t trusted_initial_count)
Definition triangles.hpp:19
void apply(const UpdateBatch &batch)
Definition triangles.hpp:26
std::uint64_t value() const noexcept
Definition triangles.hpp:24
std::uint64_t common_neighbors(VertexId a, VertexId b) const
Definition triangles.hpp:58
bool has_edge(VertexId u, VertexId v) const
IncrementalTriangleCount(DynamicGraph &graph, std::uint64_t trusted_initial_count)
Definition triangles.hpp:81
IncrementalTriangleCount(DynamicGraph &graph)
Definition triangles.hpp:79
void apply(const UpdateBatch &batch)
Definition triangles.hpp:84
void apply_updates(Graph &graph, const Batch &batch)
void for_each_neighbor(const Graph &graph, VertexId u, Fn &&fn)
bool has_edge(const Graph &graph, VertexId u, VertexId v)
constexpr bool is_directed(const Graph &graph)
std::size_t neighbor_count(const Graph &graph, VertexId u)
std::uint32_t VertexId
Definition frontier.hpp:6
constexpr std::size_t vertex_count(const Graph &graph)
bool empty() const noexcept
std::vector< EdgeUpdate > updates