lean_graph
 
Loading...
Searching...
No Matches
lean_graph.h
Go to the documentation of this file.
1#pragma once
2#include <__expected/unexpected.h>
3#include <algorithm>
4#include <functional>
5#include <limits>
6#include <optional>
7#include <queue>
8#include <set>
9#include <stack>
10#include <tuple>
11#include <unordered_map>
12#include <unordered_set>
13
17namespace lean_graph {
18// Declaration of the concept “Hashable”, which is satisfied by any type “T”
19// such that for values “a” of type “T”, the expression std::hash<T>{}(a)
20// compiles and its result is convertible to std::size_t
21//
22// TAG: Hashable DECL
23template <typename T>
24concept Hashable = requires(T a) {
25 { std::hash<T>{}(a) } -> std::convertible_to<std::size_t>;
26};
27
28// TAG: DefaultHashMap DECL
29template <class NodeType, class CounterType>
30using DefaultHashMap = std::unordered_map<NodeType, CounterType>;
31
32// TAG: Counter DECL
33template <class Aspect, class CounterType, class H> class Counter;
34
35// TAG: DiGraph DECL
38template <class NodeType, class Cost = float_t,
39 class CounterType = std::uint16_t,
41 requires Hashable<NodeType> and std::is_arithmetic_v<Cost>
42class DiGraph;
43
44// TAG: DAG DECL
47template <class NodeType, class Cost = float_t,
48 class CounterType = std::uint16_t,
50 requires Hashable<NodeType> and std::is_arithmetic_v<Cost>
51class DAG;
52
53// TAG: UniGraph DECL
54template <class NodeType, class Cost = float_t,
55 class CounterType = std::uint16_t,
57 requires Hashable<NodeType> and std::is_arithmetic_v<Cost>
58class UniGraph;
59
60// TAG: Connectivity DECL
61template <class CounterType, class H = DefaultHashMap<CounterType, CounterType>>
62class Connectivity;
63
67
68// TAG: Edge DECL
69template <class CounterType, class Cost>
70using CounterEdge = std::tuple<CounterType, CounterType, Cost>;
71template <class CounterType>
72using CounterBlankEdge = std::tuple<CounterType, CounterType>;
73template <class CounterType, class Cost>
74using CounterHalfEdge = std::tuple<CounterType, Cost>;
75
76template <class CounterType, class Cost>
77using Edge = std::tuple<CounterType, CounterType, Cost>;
78
79} // namespace lean_graph
80
84//
85//
86//
87//
88//
89//
90//
91//
92//
93//
94//
95//
96//
97//
98//
99//
100//
101//
102//
103//
104//
105//
109namespace lean_graph {
110
111// TAG: Counter DEFN
113template <class Aspect, class CounterType, class H> class Counter {
114 H counter;
115 CounterType count;
116
117public:
118 explicit Counter(CounterType start_from) : count(start_from) {}
119 bool exist(const Aspect &aspect) const {
120 return counter.find(aspect) != counter.end();
121 }
122
123 bool counter_exceeds(CounterType ct) const { return count > ct; }
124 CounterType get_counter(const Aspect &aspect) {
125 if (not exist(aspect))
126 counter[aspect] = count++;
127 return counter[aspect];
128 }
129 CounterType get_counter() const { return count; }
130};
131
132template <class CounterType, class Cost> class EdgeIte {};
134// TAG: DiGraph DEFN
135template <class NodeType, class Cost, class CounterType, class H>
136 requires Hashable<NodeType> and std::is_arithmetic_v<Cost>
137class DiGraph {
138public:
139protected:
140 std::unordered_map<CounterType, std::set<CounterHalfEdge<CounterType, Cost>>>
142
144 CounterType num_node, num_edge;
145 template <VisitOrder v>
147 CounterType from,
148 std::optional<std::reference_wrapper<std::unordered_set<CounterType>>>
149 pre_visited = std::nullopt) const -> std::vector<CounterType> {
150 using tup = std::tuple<CounterType, VisitOrder>;
151 std::stack<tup> stck;
152 std::unordered_set<CounterType> local_visited;
153 std::unordered_set<CounterType> &visited =
154 pre_visited.has_value() ? pre_visited->get() : local_visited;
155 std::vector<CounterType> result;
156 if (not existCounterNode(from))
157 return {};
158
159 stck.push(tup(from, VisitOrder::pre));
160 while (not stck.empty()) {
161 auto [current_node, visit_order] = stck.top();
162 stck.pop();
163 visited.insert(current_node);
164
165 if (visit_order == VisitOrder::pre) {
166 if constexpr (v == VisitOrder::pre)
167 result.push_back(current_node);
168
169 stck.push(tup(current_node, VisitOrder::post));
170 auto neighbors = graph.find(current_node);
171 if (neighbors == graph.end())
172 continue;
173 for (auto &[neighbor, cost] : (*neighbors).second) {
174 if (visited.contains(neighbor))
175 continue;
176 stck.push(tup(neighbor, VisitOrder::pre));
177 }
178 } else if constexpr (v == VisitOrder::post)
179 result.push_back(current_node);
180 }
181
182 return result;
183 }
184
185 template <VisitOrder v>
187 CounterType from,
188 std::optional<std::reference_wrapper<std::unordered_set<CounterType>>>
189 pre_visited = std::nullopt) const -> std::vector<CounterType> {
190 using tup = std::tuple<CounterType, VisitOrder>;
191 std::queue<tup> q;
192 std::unordered_set<CounterType> local_visited;
193 std::unordered_set<CounterType> &visited =
194 pre_visited.has_value() ? pre_visited->get() : local_visited;
195 std::vector<CounterType> result;
196 if (not existCounterNode(from))
197 return {};
198
199 q.push(tup(from, VisitOrder::pre));
200 while (not q.empty()) {
201 auto [current_node, visit_order] = q.front();
202 q.pop();
203 visited.insert(current_node);
204
205 if (visit_order == VisitOrder::pre) {
206 if constexpr (v == VisitOrder::pre)
207 result.push_back(current_node);
208
209 q.push(tup(current_node, VisitOrder::post));
210 auto neighbors = graph.find(current_node);
211 if (neighbors == graph.end())
212 continue;
213 for (auto &[neighbor, cost] : (*neighbors).second) {
214 if (visited.contains(neighbor))
215 continue;
216 q.push(tup(neighbor, VisitOrder::pre));
217 }
218 } else if constexpr (v == VisitOrder::post)
219 result.push_back(current_node);
220 }
221
222 return result;
223 }
224
225public:
226 auto registerNode(const NodeType &node) -> CounterType {
227 if (!node_counter.exist(node))
228 num_node++;
229 return node_counter.get_counter(node);
230 }
231
232 virtual auto registerEdge(CounterEdge<CounterType, Cost> edge) -> void {
233 const auto [from, to, cost] = edge;
234 if (!existEdge(edge))
235 num_edge++;
236 this->graph[from].insert({to, cost});
237 return;
238 }
239
240 virtual auto modifyEdge(CounterEdge<CounterType, Cost> edge, Cost new_cost)
241 -> std::optional<edge_error> {
242 if (not this->existEdge(edge)) // if edge doesn't exist
244
245 const auto &[from, to, old_cost] = edge;
246 const CounterEdge<CounterType, Cost> new_edge = {from, to, new_cost};
247 this->graph[from].erase({to, old_cost});
248 num_edge--;
249 if (!this->existEdge(new_edge))
250 num_edge++;
251 this->graph[from].insert({to, new_cost});
252 return std::nullopt;
253 }
254
255 auto existEdge(CounterEdge<CounterType, Cost> edge) const -> bool {
256 auto &[from, to, cost] = edge;
257 if (!existCounterNode(from))
258 return false;
259 auto s = this->graph.find(from);
260 if (s == this->graph.end())
261 return false;
262
263 return (*s).second.contains({to, cost});
264 }
267 auto from = std::get<0>(edge), to = std::get<1>(edge);
268 /*auto [from, to] = edge;*/
269 if (not existCounterNode(from))
270 return false;
271 auto s = this->graph.find(from);
272 if (s == this->graph.end())
273 return false;
274
275 auto st = (*s).second;
276 return std::find_if(st.begin(), st.end(), [=](const auto &p) {
277 return to == std::get<0>(p);
278 }) != st.end();
279 }
281 auto [from, to, cost] = edge;
282 return existBlankEdge({from, to});
283 }
284 auto existCounterNode(CounterType node) const -> bool {
285 return node_counter.counter_exceeds(node);
286 }
287
288 virtual auto edges() const -> std::vector<CounterEdge<CounterType, Cost>> {
289 decltype(edges()) result;
290 for (auto &[node, neighbors_info] : this->graph) {
291 for (auto &[to_neighbor, cost] : neighbors_info) {
292 result.push_back({node, to_neighbor, cost});
293 }
294 }
295 return result;
296 }
297
299 template <VisitOrder v>
300 [[nodiscard("\nDON'T DISCARD THE RESULT OF dfs() - DEPTH FIRST "
301 "SEARCH ON THE WHOLE GRAPH.\n")]]
302 auto dfs() const -> std::vector<CounterType> {
303 std::unordered_set<CounterType> visited;
304 std::vector<CounterType> result;
305 for (auto [node, st] : graph) {
306 if (not visited.contains(node))
307 result.insert_range(result.end(),
308 explore_dfs_protected<v>(node, visited));
309 }
310
311 return result;
312 }
313
316 template <VisitOrder v>
317 [[nodiscard("\nDON'T DISCARD THE RESULT OF bfs() - BREADTH FIRST "
318 "SEARCH ON THE WHOLE GRAPH.\n")]]
319 auto bfs() const -> std::vector<CounterType> {
320 std::unordered_set<CounterType> visited;
321 std::vector<CounterType> result;
322 for (auto [node, st] : graph) {
323 if (not visited.contains(node))
324 result.insert_range(result.end(),
325 explore_bfs_protected<v>(node, visited));
326 }
327 return result;
328 }
329
332 template <VisitOrder v>
333 [[nodiscard("\nDON'T DISCARD THE RESULT OF explore_dfs() - DEPTH FIRST "
334 "SEARCH OF A SINGULAR NODE.\n")]]
335 auto explore_dfs(CounterType from) const -> std::vector<CounterType> {
336 return explore_dfs_protected<v>(from, std::nullopt);
337 }
338
341 template <VisitOrder v>
342 [[nodiscard("\nDON'T DISCARD THE RESULT OF explore_bfs() - BREADTH FIRST "
343 "SEARCH OF A SINGULAR NODE.\n")]]
344 auto explore_bfs(CounterType from) const -> std::vector<CounterType> {
345 return explore_bfs_protected<v>(from, std::nullopt);
346 }
347
349 [[nodiscard("\nDON'T DISCARD THE RESULT OF cycles(), WHICH RETURNS A VECTOR "
350 "OF ELEMENTARY CYCLES\n")]]
351 virtual auto /* DiGraph */ cycles() const
352 -> std::vector<std::vector<CounterType>> {
353 return {};
354 }
355
360 [[nodiscard(
361 "\nDon't discard the result of djikstra's singular shorest path.\n")]]
362 virtual auto /* DiGraph */ singular_shortest_path(CounterType start,
363 CounterType end) const
364 -> std::pair<std::unordered_map<CounterType, Cost>,
365 std::unordered_map<CounterType, CounterType>> {
366 if (not existCounterNode(start))
367 return {{}, {}};
368 std::unordered_map<CounterType, Cost> dist_from_start;
369 std::unordered_map<CounterType, CounterType> prev;
370 dist_from_start[start] = 0;
371 std::priority_queue<std::tuple<Cost, CounterType>,
372 std::vector<std::tuple<Cost, CounterType>>,
373 decltype(std::greater<>())>
374 pq(std::greater<>{});
375
376 pq.emplace(dist_from_start[start], start);
377 while (not pq.empty()) {
378 auto [dist_node, node] = pq.top();
379 pq.pop();
380
381 for (auto [neighbor, cost] : (*graph.find(node)).second) {
382 if (not dist_from_start.contains(neighbor) or
383 (dist_from_start[neighbor] > dist_from_start[node] + cost)) {
384 dist_from_start[neighbor] = dist_from_start[node] + cost;
385 prev[neighbor] = node;
386 pq.emplace(dist_from_start[neighbor], neighbor);
387 }
388 }
389 }
390
391 if (not prev.contains(end))
392 return {{}, {}};
393
394 return {dist_from_start, prev};
395 }
396
402 [[nodiscard("\nDon't discard the result of bellman_ford\n")]]
403 auto bellman_ford(CounterType start) const
404 -> std::pair<std::unordered_map<CounterType, Cost>,
405 std::unordered_map<CounterType, CounterType>> const {
406 std::unordered_map<CounterType, Cost> cost_map;
407 std::unordered_map<CounterType, CounterType> prev;
408
409 auto edges = this->edges();
410
411 auto num_vertex_minus_1 = this->num_node - 1;
412 cost_map[start] = 0;
413 while (num_vertex_minus_1) {
414 for (auto &[from, to, cost] : edges) {
415 if (not cost_map.contains(from) && not cost_map.contains(to))
416 continue;
417 if (not cost_map.contains(from)) // if we don't do this, big fat ass
418 continue; // trouble of over-flowing
419
420 auto &a = cost_map[from];
421 auto b = cost_map.contains(to) ? cost_map[to]
422 : std::numeric_limits<Cost>::max();
423
424 if (a + cost < b) {
425 a = a + cost;
426 prev[to] = from;
427 }
428 }
429
430 num_vertex_minus_1--;
431 }
432 for (auto &[from, to, cost] : edges) {
433 if (not cost_map.contains(from) && not cost_map.contains(to))
434 continue;
435 if (not cost_map.contains(from))
436 continue;
437
438 auto &a = cost_map[from];
439 auto b = cost_map.contains(to) ? cost_map[to]
440 : std::numeric_limits<Cost>::max();
441
442 // INFO: negative cycle detected
443 if (a + cost < b)
444 return {};
445 }
446
447 return {cost_map, prev};
448 }
449
451 auto scc() -> std::vector<DiGraph> const;
452 template <class CT, class Cst> friend class EdgeIte;
453};
454
457
458// TAG: DAG DEFN
459template <class NodeType, class Cost, class CounterType, class H>
460 requires Hashable<NodeType> and std::is_arithmetic_v<Cost>
461class DAG : public DiGraph<NodeType, Cost, CounterType, H> {
462public:
463 // A topological sort is a reversed post order
464 auto /* DAG */ topo_sort() const -> std::vector<CounterType> {
465 auto dfs_result = this->template dfs<VisitOrder::post>();
466 std::ranges::reverse(dfs_result);
467 return dfs_result;
468 }
469
470 virtual auto /* DAG */ singular_shortest_path(CounterType start,
471 CounterType end) const
472 -> std::pair<std::unordered_map<CounterType, Cost>,
473 std::unordered_map<CounterType, CounterType>> override {
474
475 if (not this->existCounterNode(start))
476 return {{}, {}};
477 std::unordered_map<CounterType, Cost> dist_from_start;
478 std::unordered_map<CounterType, CounterType> prev;
479 dist_from_start[start] = 0;
480
481 auto linearized_graph_nodes = this->topo_sort();
482
483 for (auto node : linearized_graph_nodes) {
484 for (auto [neighbor, cost] : (*this->graph.find(node)).second) {
485
486 if (not dist_from_start.contains(neighbor) or
487 (dist_from_start[neighbor] > dist_from_start[node] + cost)) {
488 dist_from_start[neighbor] = dist_from_start[node] + cost;
489 prev[neighbor] = node;
490 }
491 }
492 }
493
494 // if in the end we don't have the end node, sth seriously wrong with you or
495 // me hahahah
496 if (not prev.contains(end))
497 return {{}, {}};
498
499 return {dist_from_start, prev};
500 }
501};
502// TAG: UniGraph DEFN
503template <class NodeType, class Cost, class CounterType, class H>
504 requires Hashable<NodeType> and std::is_arithmetic_v<Cost>
505class UniGraph : DiGraph<NodeType, Cost, CounterType, H> {
506
507public:
509 -> void override {
510 if (!this->existEdge(edge))
511 this->num_edge++;
512 const auto [from, to, cost] = edge;
513 this->graph[from].insert({to, cost});
514 this->graph[to].insert({from, cost});
515 return;
516 }
517
519 Cost new_cost)
520 -> std::optional<edge_error> override {
521 const auto &[from, to, old_cost] = edge;
522
523 decltype(edge) forward_edge = {from, to, old_cost};
524 decltype(edge) backward_edge = {to, from, old_cost};
525
527
528 if (dg::modifyEdge(forward_edge, new_cost) == edge_error::not_exist)
530
531 return dg::modifyEdge(backward_edge, new_cost);
532 }
533
534 auto /* UniGraph */ edges() const
535 -> std::vector<CounterEdge<CounterType, Cost>> override {
536 decltype(edges()) result;
537 std::unordered_set<CounterType> processed;
538 for (auto &[node, neighbors_info] : this->graph) {
539 for (auto &[to_neighbor, cost] : neighbors_info) {
540 if (node != to_neighbor && processed.contains(node))
541 continue;
542 result.push_back({node, to_neighbor, cost});
543 }
544 processed.insert(node);
545 }
546 return result;
547 }
548 auto /* UniGraph */ cycles() const
549 -> std::vector<std::vector<CounterType>> override {
550 return {};
551 }
552 auto /* UniGraph */ mst_kruskal()
553 -> std::vector<CounterEdge<CounterType, Cost>> {
555
556 auto edges = this->edges();
557 std::priority_queue<CounterEdge<CounterType, Cost>,
558 std::vector<CounterEdge<CounterType, Cost>>,
559 decltype([](auto a, auto b) {
560 return std::get<2>(a) > std::get<2>(b);
561 })>
562 pq{};
563 pq.push_range(edges);
564
565 decltype(mst_kruskal()) mst;
566 while (!pq.empty()) {
567 auto [from, to, cost] = pq.top();
568 pq.pop();
569 if (not conn.is_connected(from, to)) {
570 conn.unite(from, to);
571 mst.push_back({from, to, cost});
572 }
573 }
574
575 return mst;
576 }
577};
578// A topological sort is a reversed post order
579
580// TAG: CONNECTIVITY DEFN
582template <class CounterType, class H> class Connectivity {
583 H uf;
584 std::unordered_map<CounterType, uint32_t> rank;
585
587 CounterType /* Connectivity */ find(CounterType a) {
588 if (uf.find(a) == uf.end()) // lazy rank computation
589 return a;
590
591 if (a != uf[a])
592 uf[a] = find(uf[a]);
593 return uf[a];
594 }
595
596public:
597 bool /* Connectivity */ is_connected(CounterType a, CounterType b) {
598 return this->find(a) == this->find(b);
599 }
600
602 //
603 void /* Connectivity */ unite(CounterType a, CounterType b) {
604 auto ra = this->find(a);
605 auto rb = this->find(b);
606
607 if (ra == rb)
608 return;
609 if (rank[ra] > rank[rb])
610 uf[rb] = ra;
611 else {
612 uf[ra] = uf[rb];
613 if (rank[ra] == rank[rb])
614 rank[rb] = rank[rb] + 1;
615 }
616 }
617};
618
621} // namespace lean_graph
INFO: Connectivity is just glorified union find, this is from DPV book.
Definition lean_graph.h:582
bool is_connected(CounterType a, CounterType b)
Definition lean_graph.h:597
void unite(CounterType a, CounterType b)
INFO: Unite (Union) a with b.
Definition lean_graph.h:603
INFO: A counter class.
Definition lean_graph.h:113
Counter(CounterType start_from)
Definition lean_graph.h:118
CounterType get_counter() const
Definition lean_graph.h:129
CounterType get_counter(const Aspect &aspect)
Definition lean_graph.h:124
bool counter_exceeds(CounterType ct) const
Definition lean_graph.h:123
bool exist(const Aspect &aspect) const
Definition lean_graph.h:119
Definition lean_graph.h:461
virtual auto singular_shortest_path(CounterType start, CounterType end) const -> std::pair< std::unordered_map< CounterType, Cost >, std::unordered_map< CounterType, CounterType > > override
Definition lean_graph.h:470
auto topo_sort() const -> std::vector< CounterType >
Definition lean_graph.h:464
Definition lean_graph.h:137
auto registerNode(const NodeType &node) -> CounterType
Definition lean_graph.h:226
CounterType num_edge
Definition lean_graph.h:144
virtual auto modifyEdge(CounterEdge< CounterType, Cost > edge, Cost new_cost) -> std::optional< edge_error >
Definition lean_graph.h:240
auto scc() -> std::vector< DiGraph > const
INFO: Strongly connected components (SCC)
Counter< NodeType, CounterType, H > node_counter
Definition lean_graph.h:143
auto existEdge(CounterEdge< CounterType, Cost > edge) const -> bool
Definition lean_graph.h:255
auto explore_dfs_protected(CounterType from, std::optional< std::reference_wrapper< std::unordered_set< CounterType > > > pre_visited=std::nullopt) const -> std::vector< CounterType >
Definition lean_graph.h:146
auto existCounterNode(CounterType node) const -> bool
Definition lean_graph.h:284
auto explore_bfs(CounterType from) const -> std::vector< CounterType >
Definition lean_graph.h:344
virtual auto registerEdge(CounterEdge< CounterType, Cost > edge) -> void
Definition lean_graph.h:232
friend class EdgeIte
Definition lean_graph.h:452
virtual auto edges() const -> std::vector< CounterEdge< CounterType, Cost > >
Definition lean_graph.h:288
auto bfs() const -> std::vector< CounterType >
Definition lean_graph.h:319
virtual auto cycles() const -> std::vector< std::vector< CounterType > >
INFO: Johnson algorithm.
Definition lean_graph.h:351
auto existBlankEdge(CounterBlankEdge< CounterType > edge) const -> bool
Definition lean_graph.h:265
virtual auto singular_shortest_path(CounterType start, CounterType end) const -> std::pair< std::unordered_map< CounterType, Cost >, std::unordered_map< CounterType, CounterType > >
Definition lean_graph.h:362
auto explore_bfs_protected(CounterType from, std::optional< std::reference_wrapper< std::unordered_set< CounterType > > > pre_visited=std::nullopt) const -> std::vector< CounterType >
Definition lean_graph.h:186
auto explore_dfs(CounterType from) const -> std::vector< CounterType >
Definition lean_graph.h:335
auto existBlankEdge(CounterEdge< CounterType, Cost > edge) const -> bool
Definition lean_graph.h:280
auto dfs() const -> std::vector< CounterType >
Definition lean_graph.h:302
CounterType num_node
Definition lean_graph.h:144
std::unordered_map< CounterType, std::set< CounterHalfEdge< CounterType, Cost > > > graph
Definition lean_graph.h:141
auto bellman_ford(CounterType start) const -> std::pair< std::unordered_map< CounterType, Cost >, std::unordered_map< CounterType, CounterType > > const
Definition lean_graph.h:403
Definition lean_graph.h:132
Definition lean_graph.h:505
auto registerEdge(CounterEdge< CounterType, Cost > edge) -> void override
Definition lean_graph.h:508
auto modifyEdge(CounterEdge< CounterType, Cost > edge, Cost new_cost) -> std::optional< edge_error > override
Definition lean_graph.h:518
auto edges() const -> std::vector< CounterEdge< CounterType, Cost > > override
Definition lean_graph.h:534
auto cycles() const -> std::vector< std::vector< CounterType > > override
INFO: Johnson algorithm.
Definition lean_graph.h:548
auto mst_kruskal() -> std::vector< CounterEdge< CounterType, Cost > >
Definition lean_graph.h:552
Definition lean_graph.h:24
Definition lean_graph.h:17
VisitOrder
Definition lean_graph.h:66
@ post
Definition lean_graph.h:66
@ pre
Definition lean_graph.h:66
std::tuple< CounterType, Cost > CounterHalfEdge
Definition lean_graph.h:74
edge_error
Definition lean_graph.h:65
@ not_exist
Definition lean_graph.h:65
std::tuple< CounterType, CounterType > CounterBlankEdge
Definition lean_graph.h:72
std::tuple< CounterType, CounterType, Cost > CounterEdge
Definition lean_graph.h:70
std::unordered_map< NodeType, CounterType > DefaultHashMap
Definition lean_graph.h:30
node_error
Definition lean_graph.h:64
@ duplicate
Definition lean_graph.h:64
@ general_error
Definition lean_graph.h:64
@ not_exist
Definition lean_graph.h:64
std::tuple< CounterType, CounterType, Cost > Edge
Definition lean_graph.h:77