Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
Bellman_Ford.H
Go to the documentation of this file.
1
2/*
3 Aleph_w
4
5 Data structures & Algorithms
6 version 2.0.0b
7 https://github.com/lrleon/Aleph-w
8
9 This file is part of Aleph-w library
10
11 Copyright (c) 2002-2026 Leandro Rabindranath Leon
12
13 Permission is hereby granted, free of charge, to any person obtaining a copy
14 of this software and associated documentation files (the "Software"), to deal
15 in the Software without restriction, including without limitation the rights
16 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17 copies of the Software, and to permit persons to whom the Software is
18 furnished to do so, subject to the following conditions:
19
20 The above copyright notice and this permission notice shall be included in all
21 copies or substantial portions of the Software.
22
23 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29 SOFTWARE.
30*/
31
32
87# ifndef BELLMAN_FORD_H
88# define BELLMAN_FORD_H
89
90# include <type_traits>
91# include <limits>
92# include <vector>
93
94# include <tpl_dynListQueue.H>
95# include <tpl_dynSetTree.H>
96# include <tpl_graph_utils.H>
97# include <Tarjan.H>
98# include <ah-errors.H>
99# include <ah_init_guard.H>
100# include <cookie_guard.H>
101
102namespace Aleph
103{
170 template <class GT,
171 class Distance = Dft_Dist<GT>,
172 template <class, class> class Ait = Arc_Iterator,
173 template <class, class> class NAit = Out_Iterator,
174 class SA = Dft_Show_Arc<GT>>
176 {
178
179 using Node = typename GT::Node;
180 using Arc = typename GT::Arc;
181
182 struct Sni
183 {
185 };
186
187 struct Ni : public Sni
188 {
189 int idx; // index in the predecessor arrays
190 };
191
192 static Distance_Type &accum(Node *p) noexcept
193 {
194 return static_cast<Sni *>(NODE_COOKIE(p))->accum;
195 }
196
197 static int &idx(Node *p) noexcept
198 {
199 return static_cast<Ni *>(NODE_COOKIE(p))->idx;
200 }
201
202 // Checked addition to prevent integer overflow
204 {
205 if constexpr (std::is_integral_v<Distance_Type>)
206 {
207 // Check for positive overflow
208 ah_overflow_error_if(b > 0 && a > std::numeric_limits<Distance_Type>::max() - b)
209 << "Integer overflow in distance addition: " << a << " + " << b;
210
211 // Check for negative overflow (underflow)
212 ah_overflow_error_if(b < 0 && a < std::numeric_limits<Distance_Type>::min() - b)
213 << "Integer underflow in distance addition: " << a << " + " << b;
214 }
215
216 return a + b;
217 }
218
220 GT & g; // Non-const: algorithm modifies node/arc bits and cookies
222 bool painted = false;
223 Node *s = nullptr;
226
228 void init_simple(Node *start)
229 {
230 Init_Guard guard([this]() { uninit<Sni>(); });
231
232 typename GT::Node_Iterator it(g);
233 for (int i = 0; it.has_curr(); ++i, it.next_ne())
234 {
235 auto p = it.get_curr();
236 g.reset_bit(p, Aleph::Spanning_Tree); // set bit to zero
237 auto ptr = new Sni;
238 ptr->accum = Inf;
239 NODE_BITS(p).set_bit(Spanning_Tree, false);
240 NODE_COOKIE(p) = ptr;
241 }
242 s = start;
243 accum(s) = 0;
244 g.reset_arcs();
245
246 guard.release(); // Successful initialization, prevent cleanup
247 }
248
251 {
252 Init_Guard guard([this]()
253 {
254 uninit<Ni>();
255 arcs.cut();
256 });
257
258 const size_t n = g.get_num_nodes();
259 arcs.cut(); // Clear any previous data
260 arcs.reserve(n);
261
262 typename GT::Node_Iterator it(g);
263 for (size_t i = 0; it.has_curr(); ++i, it.next())
264 {
265 // Use touch() to ensure memory is allocated for index i
266 arcs.touch(i) = nullptr;
267 auto p = it.get_curr();
268 g.reset_bit(p, Aleph::Spanning_Tree); // set bit to zero
269 auto ptr = new Ni;
270 ptr->accum = Inf;
271 ptr->idx = static_cast<int>(i);
272 NODE_BITS(p).set_bit(Spanning_Tree, false);
273 NODE_BITS(p).set_bit(Depth_First, false); // indicates if it is in queue
274 NODE_COOKIE(p) = ptr;
275 }
276
277 s = start;
278 accum(s) = 0;
279
280 g.reset_arcs();
281
282 guard.release(); // Successful initialization, prevent cleanup
283 }
284
286 template <class Info_Type>
287 void uninit()
288 {
289 for (typename GT::Node_Iterator it(g); it.has_curr(); it.next())
290 {
291 auto p = it.get_curr();
292 delete static_cast<Info_Type *>(NODE_COOKIE(p));
293 NODE_COOKIE(p) = nullptr;
294 }
295 }
296
309 {
310 if (s == nullptr)
311 return false;
312
313 size_t num_painted_arcs = 0;
314 size_t num_painted_nodes = 0;
315
316 // Count painted arcs
317 for (Ait<GT, SA> it(g, sa); it.has_curr(); it.next_ne())
318 if (IS_ARC_VISITED(it.get_curr(), Aleph::Spanning_Tree))
320
321 // Count painted nodes and verify each has exactly one incoming painted arc
322 for (typename GT::Node_Iterator it(g); it.has_curr(); it.next_ne())
323 {
324 auto node = it.get_curr();
326 continue;
327
329
330 // Skip root node - it should have no incoming painted arcs
331 if (node == s)
332 continue;
333
334 // Count incoming painted arcs to this node
335 size_t incoming_count = 0;
336 for (Ait<GT, SA> ait(g, sa); ait.has_curr(); ait.next_ne())
337 {
338 auto arc = ait.get_curr();
340 g.get_tgt_node(arc) == node)
342 }
343
344 // Each non-root painted node must have exactly 1 incoming painted arc
345 if (incoming_count != 1)
346 return false;
347 }
348
349 // Tree property: #arcs == #nodes - 1
350 // (or num_painted_arcs < num_painted_nodes for disconnected graphs)
353 }
354
355 public:
371 painted(false), sa(__sa), dist(d)
372 {
373 // empty
374 }
375
394 {
395 if (painted)
396 {
397 // Clear Spanning_Tree bits from all nodes and arcs
398 for (typename GT::Node_Iterator it(g); it.has_curr(); it.next_ne())
399 NODE_BITS(it.get_curr()).set_bit(Aleph::Spanning_Tree, false);
400
401 for (Ait<GT, SA> it(g, sa); it.has_curr(); it.next_ne())
402 ARC_BITS(it.get_curr()).set_bit(Aleph::Spanning_Tree, false);
403 }
404
405 arcs.cut();
406 painted = false;
407 s = nullptr;
408 }
409
412 [[nodiscard]] bool has_computation() const noexcept { return s != nullptr; }
413
417
421
424 const GT &get_graph() const noexcept { return g; }
425
426 private:
429 {
430 const size_t & n = g.vsize();
431 if (n <= 1)
432 return; // Nothing to relax for empty or single-node graphs
433
434 for (size_t i = 0; i < n - 1; ++i)
435 for (Ait<GT, SA> it(g, sa); it.has_curr(); it.next_ne())
436 {
437 auto arc = it.get_curr();
438 auto src = g.get_src_node(arc);
439 const auto & accum_src = accum(src);
440 if (accum_src == Inf)
441 continue;
442
443 auto tgt = it.get_tgt_node_ne();
444 auto w = dist(arc);
445 auto sum = checked_add(accum_src, w);
446 auto & accum_tgt = accum(tgt);
447 if (sum < accum_tgt) // Relax Arc
448 {
449 const auto & index = idx(tgt);
450 arcs(index) = arc;
451 accum_tgt = sum;
452 }
453 }
454 }
455
457 static void
459 {
460 if (IS_NODE_VISITED(p, Depth_First)) // is already inside the queue?
461 return;
462 NODE_BITS(p).set_bit(Depth_First, true);
463 q.put(p);
464 }
465
468 {
469 auto ret = q.get();
471 NODE_BITS(ret).set_bit(Depth_First, false);
472 return ret;
473 }
474
477 {
478 for (NAit<GT, SA> it(src, sa); it.has_curr(); it.next_ne())
479 {
480 auto arc = it.get_curr();
481 auto arc_src = g.get_src_node(arc);
482 const auto & accum_src = accum(arc_src);
483 if (accum_src == Inf)
484 continue;
485
486 auto tgt = g.get_tgt_node(arc);
487 auto w = dist(arc);
488 auto sum = checked_add(accum_src, w);
489 auto & accum_tgt = accum(tgt);
490 if (sum < accum_tgt) // Relax Arc
491 {
492 const auto & index = idx(tgt);
493 arcs(index) = arc;
494 accum_tgt = sum;
495 put_in_queue(q, tgt);
496 }
497 }
498 }
499
502 { // paint the involved nodes and arcs
503 const size_t n = g.vsize();
504 for (size_t i = 0; i < n; ++i)
505 {
506 auto arc = arcs(i);
507 if (arc == nullptr)
508 continue;
509
510 ARC_BITS(arc).set_bit(Aleph::Spanning_Tree, true);
511 auto src = g.get_src_node(arc);
512 auto tgt = g.get_tgt_node(arc);
513 NODE_BITS(src).set_bit(Aleph::Spanning_Tree, true);
514 NODE_BITS(tgt).set_bit(Aleph::Spanning_Tree, true);
515 }
516 NODE_BITS(s).set_bit(Aleph::Spanning_Tree, true);
517
519
520 painted = true;
521 }
522
526 {
527 bool negative_cycle = false;
528 for (Ait<GT, SA> it(g, sa); it.has_curr(); it.next_ne())
529 {
530 auto arc = it.get_curr();
531 auto src = g.get_src_node(arc);
532 auto & accum_src = accum(src);
533 if (accum_src == Inf)
534 continue;
535
536 auto tgt = g.get_tgt_node(arc);
537 auto d = dist(arc);
538 auto & accum_tgt = accum(tgt);
539 auto sum = checked_add(accum_src, d);
540 if (sum < accum_tgt)
541 {
542 negative_cycle = true;
543 const auto & index = idx(tgt);
544 arcs(index) = arc;
545 accum_tgt = sum;
546 }
547 }
548 return negative_cycle;
549 }
550
553 {
554 for (Ait<GT, SA> it(g, sa); it.has_curr(); it.next_ne())
555 {
556 auto arc = it.get_curr();
557 auto src = g.get_src_node(arc);
558 auto & accum_src = accum(src);
559 if (accum_src == Inf)
560 continue;
561
562 auto tgt = g.get_tgt_node(arc);
563 auto d = dist(arc);
564 auto & accum_tgt = accum(tgt);
565 auto sum = checked_add(accum_src, d);
566 if (sum < accum_tgt)
567 return true;
568 }
569 return false;
570 }
571
573 void link_cookies_and_free(typename GT::Node *start) noexcept
574 {
575 uninit<Ni>();
576
577 // Construct the inverted paths to the start origin node
578 const size_t n = g.vsize();
579 for (size_t i = 0; i < n; ++i)
580 {
581 auto arc = arcs(i);
582 if (arc == nullptr)
583 continue;
584
585 auto tgt = g.get_tgt_node(arc);
586 NODE_COOKIE(tgt) = g.get_src_node(arc);
587 }
588
589 NODE_COOKIE(start) = nullptr; // just in case there is a negative cycle
590 }
591
592 public:
603 {
604 ah_domain_error_if(start == nullptr)
605 << "start node cannot be null";
606
607 init_with_indexes(start);
608
609 relax_arcs();
610 const bool negative_cycle = last_relax_and_prepare_check_negative_cycle();
611
612 // Only paint the tree if there's no negative cycle
613 // A negative cycle makes the shortest path tree meaningless
614 if (not negative_cycle)
615 paint_tree();
616
618
619 return negative_cycle;
620 }
621
635 {
636 ah_domain_error_if(start == nullptr)
637 << "start node cannot be null";
638
639 init_with_indexes(start);
640
641 const auto & n = g.get_num_nodes();
642
644
646 Node *sentinel = &__sentinel;
647
648 put_in_queue(q, s);
649 put_in_queue(q, sentinel);
650
651 for (size_t i = 0; not q.is_empty();)
652 {
653 auto src = get_from_queue(q);
654 if (src == sentinel) // Is the sentinel removed?
655 {
656 if (i++ > n)
657 break;
658
659 put_in_queue(q, sentinel);
660 }
661 else
662 relax_arcs(src, q);
663 }
664
665 const bool negative_cycle = last_relax_and_prepare_check_negative_cycle();
666
667 // Only paint the tree if there's no negative cycle
668 // A negative cycle makes the shortest path tree meaningless
669 if (not negative_cycle)
670 paint_tree();
671
673
674 return negative_cycle;
675 }
676
677 private:
681 {
682 // RAII guard to ensure cleanup on exception
683 struct Dummy_Guard
684 {
685 GT & graph;
686 Node *dummy;
687 std::vector<Arc *> inserted_arcs;
688 bool released = false;
689
690 explicit Dummy_Guard(GT & g, Node *d) : graph(g), dummy(d)
691 {
692 inserted_arcs.reserve(g.get_num_nodes());
693 }
694
696 {
697 if (not released)
698 {
699 // Remove all inserted arcs
700 for (auto arc: inserted_arcs)
701 graph.remove_arc(arc);
702
703 // Remove dummy node
704 if (dummy != nullptr)
705 graph.remove_node(dummy);
706 }
707 }
708
709 void add_arc(Arc *a) { inserted_arcs.push_back(a); }
710 void release() { released = true; }
711
712 Dummy_Guard(const Dummy_Guard &) = delete;
713
714 Dummy_Guard & operator=(const Dummy_Guard &) = delete;
715 };
716
717 s = g.insert_node(typename GT::Node_Type());
719
720 for (typename GT::Node_Iterator it(g); it.has_curr(); it.next_ne())
721 {
722 auto p = it.get_curr();
723 if (p == s)
724 continue;
725 auto a = g.insert_arc(s, p);
726 guard.add_arc(a);
728 }
729
730 guard.release(); // Successful creation, prevent cleanup
731 return s;
732 }
733
735 template <class Info_Type>
737 {
738 delete static_cast<Info_Type *>(NODE_COOKIE(p));
739 g.remove_node(p);
740 }
741
742 public:
750 {
751 ah_domain_error_if(start == nullptr)
752 << "start node cannot be null";
753
754 init_with_indexes(start);
755 // Note: s and accum(s) already set by init_with_indexes
756
757 relax_arcs();
758 const bool negative_cycle = last_relax_and_test_negative_cycle();
759 uninit<Ni>();
760
761 return negative_cycle;
762 }
763
775 {
777
778 // RAII guard ensures dummy node removal even on exception
779 struct Global_Cycle_Guard
780 {
783 bool released = false;
784
786
788 {
789 if (not released and dummy_node != nullptr)
790 {
791 // Clean up: remove dummy node and its arcs
792 bf.remove_dummy_node<Ni>(dummy_node);
793 }
794 }
795
796 void release() { released = true; }
797 };
798
800
802 guard.release();
804
805 return ret;
806 }
807
808 private:
811 {
813
814 // we map because Tarjan algorithm modifies cookies
816 for (typename GT::Node_Iterator it(aux); it.has_curr(); it.next_ne())
817 {
818 auto p = it.get_curr();
819 table.insert(p, static_cast<Node *>(NODE_COOKIE(p)));
820 }
821
822 // Save and restore cookies around Tarjan call (Tarjan modifies cookies)
823 Cookie_Saver<GT> cookie_saver(aux, true, false); // only save node cookies
824
825 // Clear cookies for Tarjan's use
826 for (typename GT::Node_Iterator it(aux); it.has_curr(); it.next_ne())
827 NODE_COOKIE(it.get_curr()) = nullptr;
828
829 if (Path<GT> path(aux); Tarjan_Connected_Components<GT, NAit, SA>(sa).compute_cycle(aux, path))
830 {
831 Path<GT> ret(g);
832 for (typename Path<GT>::Iterator it(path); it.has_current_node();
833 it.next_ne())
834 ret.append_directed(static_cast<Node *>(table.find(it.get_current_node_ne())));
835 return ret;
836 }
837
838 return Path<GT>(g);
839 }
840
841 public:
854 {
855 ah_domain_error_if(start == nullptr)
856 << "start node cannot be null";
857
858 init_with_indexes(start);
859
860 relax_arcs();
861 const bool negative_cycle = last_relax_and_prepare_check_negative_cycle();
862 if (not negative_cycle)
863 {
865 return Path<GT>(g);
866 }
867
869 if (ret.is_empty())
870 WARNING("Serious inconsistency. Bellman-Ford algorithm has detected\n"
871 "a negative cycle, but Tarjan algorithm executed on partial\n"
872 "graph has not found such cycle\n\n"
873 "Be very careful, this is provably a bug");
874
876 return ret;
877 }
878
885 {
886 auto start = create_dummy_node();
887
888 // RAII guard ensures dummy node removal even on exception
889 Init_Guard guard([this, start]()
890 {
892 });
893
894 auto ret_val = test_negative_cycle(start);
895 guard.release();
897 return ret_val;
898 }
899
943 std::tuple<Path<GT>, size_t>
944 search_negative_cycle(Node *start, double it_factor, const size_t step)
945 {
946 ah_domain_error_if(start == nullptr)
947 << "start node cannot be null";
948
949 init_with_indexes(start);
950
951 const auto & n = g.get_num_nodes();
954 Node *sentinel = &__sentinel;
955 put_in_queue(q, s);
956 put_in_queue(q, sentinel);
957
958 double threshold = it_factor * n;
959 Path<GT> ret(g);
960
961 size_t i = 0;
962 while (not q.is_empty())
963 {
964 auto src = get_from_queue(q);
965 if (src == sentinel)
966 {
967 if (i++ > n)
968 break;
969
970 put_in_queue(q, sentinel);
971 if (i >= threshold) // must I search negative cycles?
972 {
974 if (not ret.is_empty()) // negative cycle found?
975 {
977 return std::make_tuple(std::forward<Path<GT>>(ret), i);
978 }
979 threshold += step;
980 }
981 }
982 else
983 relax_arcs(src, q);
984 }
985
986 const bool negative_cycle = last_relax_and_prepare_check_negative_cycle();
987 if (negative_cycle)
988 {
990 if (ret.is_empty())
991 WARNING("Serious inconsistency. Bellman-Ford algorithm has detected\n"
992 "a negative cycle, but Tarjan algorithm executed on partial\n"
993 "graph has not found such cycle\n\n"
994 "Be very careful, this provably is a bug");
995 }
996
998 return std::make_tuple(std::forward<Path<GT>>(ret), i);
999 }
1000
1009 {
1010 ah_domain_error_if(start == nullptr)
1011 << "start node cannot be null";
1012
1013 init_with_indexes(start);
1014
1015 const auto & n = g.get_num_nodes();
1018 Node *sentinel = &__sentinel;
1019 put_in_queue(q, s);
1020 put_in_queue(q, sentinel);
1021
1022 Path<GT> ret(g);
1023 for (size_t i = 0; not q.is_empty(); /* nothing */)
1024 {
1025 auto src = get_from_queue(q);
1026 if (src == sentinel)
1027 {
1028 if (i++ > n)
1029 break;
1030
1031 put_in_queue(q, sentinel);
1032 }
1033 else
1034 relax_arcs(src, q);
1035 }
1036
1037 const bool negative_cycle = last_relax_and_prepare_check_negative_cycle();
1038 if (negative_cycle)
1039 {
1041 if (ret.is_empty())
1042 WARNING("Serious inconsistency. Bellman-Ford algorithm has detected\n"
1043 "a negative cycle, but Tarjan algorithm executed on partial\n"
1044 "graph has not found such cycle\n\n"
1045 "Be very careful, this provably is a bug");
1046 }
1047
1049
1050 return ret;
1051 }
1052
1063 std::tuple<Path<GT>, size_t> search_negative_cycle(double it_factor,
1064 const size_t step)
1065 {
1066 auto start = create_dummy_node();
1067
1068 // RAII guard ensures dummy node removal even on exception
1069 Init_Guard guard([this, start]()
1070 {
1071 remove_dummy_node<Ni>(start);
1072 });
1073
1075 guard.release();
1076 remove_dummy_node<Ni>(start);
1077 return ret_val;
1078 }
1079
1084 {
1085 auto start = create_dummy_node();
1086
1087 // RAII guard ensures dummy node removal even on exception
1088 Init_Guard guard([this, start]()
1089 {
1090 remove_dummy_node<Ni>(start);
1091 });
1092
1093 auto ret_val = search_negative_cycle(start);
1094 guard.release();
1095 remove_dummy_node<Ni>(start);
1096 return ret_val;
1097 }
1098
1099
1110 {
1112 << "Spanning tree has not been painted";
1113
1114 return arcs;
1115 }
1116
1128 {
1130 << "Graph has not been previously painted";
1131
1132 ah_domain_error_if(node == nullptr)
1133 << "node cannot be null";
1134
1135 // Follow predecessor chain to verify reachability
1136 auto curr = node;
1137 while (curr != s)
1138 {
1139 auto parent = static_cast<Node *>(NODE_COOKIE(curr));
1140 ah_domain_error_if(parent == nullptr)
1141 << "Node is not reachable from start node";
1142 curr = parent;
1143 }
1144
1145 // Compute distance by following the path
1146 Distance_Type total = 0;
1147 curr = node;
1148 while (curr != s)
1149 {
1150 auto parent = static_cast<Node *>(NODE_COOKIE(curr));
1151
1152 // Find the arc from parent to curr
1153 for (NAit<GT, SA> it(parent, sa); it.has_curr(); it.next_ne())
1154 if (auto arc = it.get_curr(); g.get_tgt_node(arc) == curr &&
1156 {
1157 total = checked_add(total, dist(arc));
1158 break;
1159 }
1160 curr = parent;
1161 }
1162 return total;
1163 }
1164
1174 void build_tree(GT & tree, bool with_map = true)
1175 {
1177 << "Spanning tree has not been painted";
1178
1179 clear_graph(tree);
1180
1182 for (typename GT::Node_Iterator it(g); it.has_curr(); it.next_ne())
1183 {
1184 auto gp = it.get_curr();
1185 auto tp = tree.insert_node(gp->get_info());
1186 table.insert(gp, tp);
1187 }
1188
1189 for (typename GT::Node_Iterator it(g); it.has_curr(); it.next_ne())
1190 {
1191 auto gtgt = it.get_curr();
1192 auto gsrc = static_cast<Node *>(NODE_COOKIE(gtgt));
1193 if (gsrc == nullptr)
1194 continue; // This is the source node of the spanning tree
1195
1196 // Find the spanning tree arc from gsrc to gtgt
1197 Arc *garc = nullptr;
1198 for (Out_Iterator<GT, SA> ait(gsrc, sa); ait.has_curr(); ait.next_ne())
1199 {
1200 auto a = ait.get_curr();
1202 {
1203 garc = a;
1204 break;
1205 }
1206 }
1207 ah_logic_error_unless(garc != nullptr)
1208 << "Arc not found between nodes in spanning tree";
1209
1210 auto tsrc_ptr = table.search(gsrc);
1211 auto ttgt_ptr = table.search(gtgt);
1212 ah_logic_error_unless(tsrc_ptr) << "Source node not found in mapping table";
1213 ah_logic_error_unless(ttgt_ptr) << "Target node not found in mapping table";
1214 auto tarc = tree.insert_arc(tsrc_ptr->second, ttgt_ptr->second,
1215 garc->get_info());
1216 if (with_map)
1218 }
1219
1220 if (with_map)
1221 table.for_each([](const auto & p) { GT::map_nodes(p.first, p.second); });
1222 }
1223
1231 {
1233 return not cycle.is_empty();
1234 }
1235
1242 {
1244 return not cycle.is_empty();
1245 }
1246
1255 {
1256 ah_domain_error_if(not painted) << "Graph has not been previously painted";
1257
1258 return Aleph::get_min_path<GT, Distance>(s, end, path);
1259 }
1260
1271 {
1273
1274 Init_Guard guard([this, dummy]()
1275 {
1276 uninit<Ni>();
1277 arcs.cut();
1278 if (dummy != nullptr)
1280 });
1281
1283
1284 const auto & n = g.get_num_nodes();
1285
1287
1289 Node *sentinel = &__sentinel;
1290
1291 put_in_queue(q, s);
1292 put_in_queue(q, sentinel);
1293
1294 for (size_t i = 0; not q.is_empty(); /* nothing */)
1295 {
1296 auto src = get_from_queue(q);
1297 if (src == sentinel) // Is the sentinel removed?
1298 {
1299 if (i++ > n)
1300 break;
1301 put_in_queue(q, sentinel);
1302 }
1303 else
1304 relax_arcs(src, q);
1305 }
1306
1307 const bool negative_cycle = last_relax_and_prepare_check_negative_cycle();
1308
1310
1311 // build mapping if there are no negative cycles
1312 if (not negative_cycle)
1313 for (auto it = g.get_node_it(); it.has_curr(); it.next_ne())
1314 {
1315 auto p = it.get_curr();
1316 if (p == dummy) // Skip the dummy node
1317 continue;
1318 ret.insert(p, accum(p));
1319 }
1320
1321 // Manual cleanup before throwing (guard will handle cleanup on exception)
1322 uninit<Ni>();
1323 arcs.cut();
1325 guard.release(); // Prevent double cleanup
1326
1327 ah_domain_error_if(negative_cycle) << "negative cycles detected";
1328
1329 return ret;
1330 }
1331 };
1332
1351 template <class GT,
1352 class Distance = Dft_Dist<GT>,
1353 template <class, class> class Ait = Arc_Iterator,
1354 template <class, class> class NAit = Node_Arc_Iterator,
1355 class SA = Dft_Show_Arc<GT>>
1357 {
1368 bool operator ()(GT & g, Path<GT> & path,
1369 Distance & d, SA & sa) const
1370 {
1372 test_negative_cycle(path);
1373 }
1374
1375 bool operator ()(GT & g, Path<GT> & path,
1376 Distance && d = Distance(), SA && sa = SA()) const
1377 {
1379 test_negative_cycle(path);
1380 }
1381
1393 bool operator ()(GT & g, typename GT::Node *s,
1394 Path<GT> & path, Distance & d, SA & sa) const
1395 {
1397 test_negative_cycle(s, path);
1398 }
1399
1400 bool operator ()(GT & g, typename GT::Node *s,
1401 Path<GT> & path, Distance && d = Distance(),
1402 SA && sa = SA()) const
1403 {
1405 test_negative_cycle(s, path);
1406 }
1407
1417 Distance & d, SA & sa) const
1418 {
1420 search_negative_cycle(s);
1421 }
1422
1425 Distance && d = Distance(), SA && sa = SA()) const
1426 {
1428 search_negative_cycle(s);
1429 }
1430
1438 Path<GT> operator ()(GT & g, Distance & d, SA & sa) const
1439 {
1441 search_negative_cycle();
1442 }
1443
1445 Path<GT>
1446 operator ()(GT & g, Distance && d = Distance(), SA && sa = SA()) const
1447 {
1449 search_negative_cycle();
1450 }
1451 };
1452} // end namespace Aleph
1453
1454# endif // BELLMAN_FORD_H
Tarjan's algorithm for strongly connected components.
Exception handling system with formatted messages for Aleph-w.
#define ah_logic_error_unless(C)
Throws std::logic_error if condition does NOT hold.
Definition ah-errors.H:309
#define ah_overflow_error_if(C)
Throws std::overflow_error if condition holds.
Definition ah-errors.H:463
#define ah_domain_error_if(C)
Throws std::domain_error if condition holds.
Definition ah-errors.H:522
#define WARNING(format, args...)
Print a warning message (no-op when MESSAGES is not defined).
Definition ahDefs.H:262
RAII guard for exception-safe resource cleanup.
long double w
Definition btreepic.C:153
Bellman-Ford algorithm for shortest paths with negative weights.
Distance_Type get_distance(typename GT::Node *node)
Get the accumulated distance to a node from a previously painted tree.
DynArray< Arc * > extract_min_spanning_tree()
Extract the shortest paths tree in a compressed form.
void init_with_indexes(Node *start)
Initialize node cookies with predecessor tracking for path reconstruction.
bool has_computation() const noexcept
Check if a shortest-path tree has been computed or painted.
void uninit()
Release the memory associated with the node cookies.
void relax_arcs() noexcept
Relax all arcs n-1 times (standard Bellman-Ford).
void init_simple(Node *start)
Initialize node cookies for simple mode (without predecessor tracking).
bool faster_paint_spanning_tree(Node *start)
Faster shortest paths tree painting from a start node.
void remove_dummy_node(Node *p)
Remove a dummy node and clean up its cookie.
void link_cookies_and_free(typename GT::Node *start) noexcept
Free node cookies and set up predecessor pointers for path reconstruction.
DynArray< typename GT::Arc * > arcs
const Distance_Type Inf
DynMapTree< Node *, Distance_Type > compute_nodes_weights()
Returns a mapping with the shortest distances to each node obtained after executing the Bellman-Ford ...
bool test_negative_cycle(typename GT::Node *s, Path< GT > &cycle)
Test for negative cycle and return it if found.
bool last_relax_and_prepare_check_negative_cycle() noexcept
Perform one more relaxation pass and check for negative cycle.
static GT::Node * get_from_queue(DynListQueue< typename GT::Node * > &q)
Remove a node from the queue and clear its in-queue flag.
bool paint_spanning_tree(Node *start)
Paint the shortest paths tree from a start node.
Path< GT > search_negative_cycle(Node *start)
Searches a negative cycle using the faster version of Bellman-Ford algorithm (SPFA variant).
Path< GT > search_negative_cycle()
Path< GT > test_negative_cycle()
Searches and returns a negative cycle (if it exists).
void paint_tree() noexcept
Paint the spanning tree nodes and arcs with the Spanning_Tree bit.
std::tuple< Path< GT >, size_t > search_negative_cycle(double it_factor, const size_t step)
void clear() noexcept
Clear the painted state and reset internal data structures.
typename GT::Node Node
bool is_painted() const noexcept
Check if a shortest-path tree has been painted.
bool has_negative_cycle()
Test if a negative cycle exists anywhere in the graph.
bool has_negative_cycle(Node *start)
Test if a negative cycle exists starting from a specific node.
void relax_arcs(typename GT::Node *src, DynListQueue< typename GT::Node * > &q)
Relax outgoing arcs from a source node (SPFA variant).
static void put_in_queue(DynListQueue< typename GT::Node * > &q, typename GT::Node *p)
Insert a node into the queue if not already present (SPFA optimization).
static Distance_Type & accum(Node *p) noexcept
Node * create_dummy_node()
Create a dummy node connected to all nodes with zero-weight edges.
bool check_painted_arcs() noexcept
Check that painted arcs form a valid spanning tree structure.
Distance_Type get_min_path(typename GT::Node *end, Path< GT > &path)
Extract the shortest path to a node from a previously painted tree.
bool test_negative_cycle(Path< GT > &cycle)
Test for negative cycle anywhere in the graph.
static int & idx(Node *p) noexcept
Bellman_Ford(GT &__g, Distance d=Distance(), SA __sa=SA())
Construct a Bellman-Ford executor.
const GT & get_graph() const noexcept
Get reference to the graph.
Node * get_start_node() const noexcept
Get the start node of the last computation.
Path< GT > test_negative_cycle(Node *start)
Search a negative cycle on all possible paths starting from start node.
std::tuple< Path< GT >, size_t > search_negative_cycle(Node *start, double it_factor, const size_t step)
Searches a negative cycle using the faster version of Bellman-Ford algorithm and iteratively searchin...
Distance_Type checked_add(const Distance_Type &a, const Distance_Type &b) const
Path< GT > search_negative_cycle_on_partial_graph()
Build spanning tree from arcs and search for cycle using Tarjan's algorithm.
void build_tree(GT &tree, bool with_map=true)
Extract from the graph a previously painted shortest paths tree.
typename GT::Arc Arc
Distance::Distance_Type Distance_Type
bool last_relax_and_test_negative_cycle() noexcept
Perform one more relaxation pass to detect (but not prepare) negative cycle.
RAII guard that saves and restores graph cookies.
Default distance accessor for arc weights.
void cut(const size_t new_dim=0)
Cut the array to a new dimension; that is, it reduces the dimension of array and frees the remaining ...
T & touch(const size_t i)
Touch the entry i.
void reserve(const size_t l, const size_t r)
Allocate a range of entries.
Dynamic queue of elements of generic type T based on single linked list.
T & put(const T &data)
The type of element.
T get()
Remove the oldest item of the queue.
bool is_empty() const noexcept
Return true if this is empty.
T & insert(const T &item)
Insert a new item by copy.
Definition htlist.H:1502
Generic key-value map implemented on top of a binary search tree.
Pair * search(const Key &key) const noexcept
Collect all keys.
Pair * insert(const Key &key, const Data &data)
Insert a key-value pair.
Data & find(const Key &key)
Find the value associated with key.
constexpr bool is_empty() const noexcept
Return true if list is empty.
Definition htlist.H:523
RAII guard for graph algorithm initialization.
virtual Node * insert_node(Node *node) noexcept
Insertion of a node already allocated.
Definition tpl_graph.H:524
virtual void remove_node(Node *node) noexcept
Remove a node from the graph and free its memory.
Definition tpl_graph.H:543
typename Node::Node_Type Node_Type
The arc class type.
Definition tpl_graph.H:436
virtual void remove_arc(Arc *arc) noexcept
Remove an arc from the graph and free it.
Definition tpl_graph.H:649
Graph_Arc< int > Arc
The node class type.
Definition tpl_graph.H:433
Arc * insert_arc(Node *src_node, Node *tgt_node, void *a)
Definition tpl_graph.H:604
Filtered iterator for outcoming arcs of a node.
Definition tpl_graph.H:1750
Iterator on nodes and arcs of a path.
Definition tpl_graph.H:3201
bool has_current_node() const noexcept
Return true if the iterator has a current node.
Definition tpl_graph.H:3314
Path on a graph.
Definition tpl_graph.H:2669
void for_each(Operation &operation)
Traverse all the container and performs an operation on each element.
Definition ah-dry.H:685
void reset_arcs() const
Reset all the arcs of graph (the control bits, the state, the counter and the cookie)
Definition graph-dry.H:927
Node * get_src_node(Arc *arc) const noexcept
Return the source node of arc (only for directed graphs)
Definition graph-dry.H:731
constexpr size_t get_num_nodes() const noexcept
Return the total of nodes of graph.
Definition graph-dry.H:695
static void map_arcs(A1 *p, A2 *q) noexcept
Map the arcs through their cookies.
Definition graph-dry.H:1026
void reset_bit(Node *node, int bit) const noexcept
Reset the bit of node (to zero)
Definition graph-dry.H:801
constexpr size_t vsize() const noexcept
Definition graph-dry.H:698
auto get_node_it() const noexcept
Obtains an iterator to the nodes of graph.
Definition graph-dry.H:2780
Node * get_tgt_node(Arc *arc) const noexcept
Return the target node of arc (only for directed graphs)
Definition graph-dry.H:737
static void map_nodes(N1 *p, N2 *q) noexcept
Map the nodes through their cookies.
Definition graph-dry.H:995
RAII guards for graph node/arc cookies.
List_Graph< Graph_Node< int >, Graph_Arc< int > > GT
__gmp_expr< typename __gmp_resolve_expr< T, V >::value_type, __gmp_binary_expr< __gmp_expr< T, U >, __gmp_expr< V, W >, __gmp_max_function > > max(const __gmp_expr< T, U > &expr1, const __gmp_expr< V, W > &expr2)
Definition gmpfrxx.h:4110
#define IS_NODE_VISITED(p, bit)
Determine whether the control bit is set or not to one.
#define ARC_BITS(p)
Return the control bits of arc p.
#define NODE_COOKIE(p)
Return the node cookie
void clear_graph(GT &g) noexcept
Clean a graph: all its nodes and arcs are removed and freed.
Definition tpl_graph.H:3549
#define IS_ARC_VISITED(p, bit)
Determine whether the bit field is or not set to one.
#define NODE_BITS(p)
Get the control bits of a node.
@ Depth_First
Definition aleph-graph.H:73
@ Spanning_Tree
Definition aleph-graph.H:79
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
DynList< T > maps(const C &c, Op op)
Classic map operation.
T sum(const Container &container, const T &init=T{})
Compute sum of all elements.
STL namespace.
Filtered iterator on all the arcs of a graph.
Definition tpl_graph.H:1164
Detects if a negative cycle exists and eventually computes it.
bool operator()(GT &g, Path< GT > &path, Distance &d, SA &sa) const
Invokes the detection and calculation of a negative cycle.
Default filter for filtered iterators on arcs.
Definition tpl_graph.H:1000
Filtered iterator of adjacent arcs of a node.
Definition tpl_graph.H:1119
Distance accessor.
static void set_zero(Arc *a)
TestDigraph::Arc * add_arc(TestDigraph &g, TestDigraph::Node *src, TestDigraph::Node *tgt, int value=0)
Dynamic queue implementation based on linked lists.
Dynamic set implementations based on balanced binary search trees.
Utility algorithms and operations for graphs.