VeloGraphX
High-performance dynamic graph analytics in C++20
Loading...
Searching...
No Matches
weighted_sssp.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <algorithm>
4#include <cstdint>
5#include <functional>
6#include <queue>
7#include <stdexcept>
8#include <unordered_set>
9#include <utility>
10#include <vector>
11
15
16namespace velographx {
17
18template <class Graph>
20 public:
21 static constexpr std::uint64_t kInf = incremental_detail::kDijkstraInf;
22
24 : graph_(graph), source_(source) {
25 recompute();
26 }
27
28 [[nodiscard]] const std::vector<std::uint64_t>& distances() const noexcept { return dist_; }
29
30 void apply(const WeightedUpdateBatch& batch) {
31 if (batch.empty()) return;
32
33 // Validate before calling an arbitrary graph backend so a rejected weight
34 // cannot leave a partially applied foreign graph.
35 for (const auto& op : batch.updates) {
36 if (op.src != op.dst && op.add && op.weight >= kInf) {
37 throw std::invalid_argument(
38 "edge weight exceeds the representable finite-distance domain");
39 }
40 }
41
42 const auto canonical = canonicalize(batch);
43 bool requires_recompute = false;
44 for (const auto& op : canonical.updates) {
45 const auto old_weight = edge_weight(graph_, op.src, op.dst);
46 if (!op.add) {
47 if (old_weight.has_value()) {
48 requires_recompute = true;
49 break;
50 }
51 continue;
52 }
53 if (old_weight && op.weight > *old_weight) {
54 requires_recompute = true;
55 break;
56 }
57 }
58
59 apply_updates(graph_, batch);
60 if (requires_recompute) recompute();
61 else relax_from_updates(canonical);
62 }
63
64 void recompute() {
66 vertex_count(graph_), source_, dist_,
67 [&](VertexId u, auto&& relax) {
68 for_each_weighted_neighbor(graph_, u, [&](VertexId v, auto w) { relax(v, w); });
69 });
70 }
71
72 private:
73 static std::uint64_t edge_key(VertexId u, VertexId v, bool directed) noexcept {
74 if (!directed && v < u) std::swap(u, v);
75 return (static_cast<std::uint64_t>(u) << 32U) | static_cast<std::uint64_t>(v);
76 }
77
78 WeightedUpdateBatch canonicalize(const WeightedUpdateBatch& batch) const {
79 WeightedUpdateBatch out;
80 out.updates.reserve(batch.updates.size());
81 std::unordered_set<std::uint64_t> seen;
82 seen.reserve(batch.updates.size() * 2 + 1);
83
84 for (auto it = batch.updates.rbegin(); it != batch.updates.rend(); ++it) {
85 if (it->src == it->dst) continue;
86 auto op = *it;
87 if (!is_directed(graph_) && op.dst < op.src) std::swap(op.src, op.dst);
88 if (seen.insert(edge_key(op.src, op.dst, is_directed(graph_))).second) {
89 out.updates.push_back(op);
90 }
91 }
92 std::reverse(out.updates.begin(), out.updates.end());
93 return out;
94 }
95
96 void relax_from_updates(const WeightedUpdateBatch& batch) {
97 if (dist_.size() < vertex_count(graph_)) dist_.resize(vertex_count(graph_), kInf);
98
99 using Item = std::pair<std::uint64_t, VertexId>;
100 std::priority_queue<Item, std::vector<Item>, std::greater<Item>> queue;
101
102 for (const auto& op : batch.updates) {
103 if (!op.add || op.src >= dist_.size() || op.dst >= dist_.size()) continue;
104 const auto final_weight = edge_weight(graph_, op.src, op.dst);
105 if (!final_weight) continue;
106 const auto weight = *final_weight;
107
108 if (dist_[op.src] != kInf && weight <= kInf - dist_[op.src]) {
109 const auto candidate = dist_[op.src] + weight;
110 if (candidate < dist_[op.dst]) {
111 dist_[op.dst] = candidate;
112 queue.push({candidate, op.dst});
113 }
114 }
115 if (!is_directed(graph_) && dist_[op.dst] != kInf && weight <= kInf - dist_[op.dst]) {
116 const auto reverse_candidate = dist_[op.dst] + weight;
117 if (reverse_candidate < dist_[op.src]) {
118 dist_[op.src] = reverse_candidate;
119 queue.push({reverse_candidate, op.src});
120 }
121 }
122 }
123
125 dist_, queue,
126 [&](VertexId u, auto&& relax) {
127 for_each_weighted_neighbor(graph_, u, [&](VertexId v, auto w) { relax(v, w); });
128 });
129 }
130
131 Graph& graph_;
132 VertexId source_;
133 std::vector<std::uint64_t> dist_;
134};
135
137
138} // namespace velographx
void apply(const WeightedUpdateBatch &batch)
const std::vector< std::uint64_t > & distances() const noexcept
static constexpr std::uint64_t kInf
BasicIncrementalWeightedSSSP(Graph &graph, VertexId source)
void propagate_dijkstra(std::vector< std::uint64_t > &dist, std::priority_queue< std::pair< std::uint64_t, VertexId >, std::vector< std::pair< std::uint64_t, VertexId > >, std::greater< std::pair< std::uint64_t, VertexId > > > &queue, NeighborEnumerator &&enumerate)
Definition dijkstra.hpp:17
constexpr std::uint64_t kDijkstraInf
Definition dijkstra.hpp:14
void recompute_dijkstra(std::size_t vertex_count, VertexId source, std::vector< std::uint64_t > &dist, NeighborEnumerator &&enumerate)
Definition dijkstra.hpp:38
void apply_updates(Graph &graph, const Batch &batch)
auto edge_weight(const Graph &graph, VertexId u, VertexId v)
constexpr bool is_directed(const Graph &graph)
void for_each_weighted_neighbor(const Graph &graph, VertexId u, Fn &&fn)
std::uint32_t VertexId
Definition frontier.hpp:6
constexpr std::size_t vertex_count(const Graph &graph)
std::vector< WeightedEdgeUpdate > updates