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
72#ifndef BELLMAN_FORD_H
73#define BELLMAN_FORD_H
74
75# include <ah-graph-concepts.H>
76
77#include <type_traits>
78#include <limits>
79#include <vector>
80
81#include <tpl_dynListQueue.H>
82#include <tpl_dynSetTree.H>
83#include <tpl_graph_utils.H>
84#include <Tarjan.H>
85#include <ah-errors.H>
86#include <ah_init_guard.H>
87#include <cookie_guard.H>
88
89namespace Aleph {
156template <AlephGraph GT,
158 template <class, class> class Ait = Arc_Iterator,
159 template <class, class> class NAit = Out_Iterator,
162{
164
165 using Node = typename GT::Node;
166 using Arc = typename GT::Arc;
167
168 struct Sni
169 {
171 };
172
173 struct Ni : public Sni
174 {
175 int idx; // index in the predecessor arrays
176 };
177
178 static Distance_Type &accum(Node *p) noexcept
179 {
180 return static_cast<Sni *>(NODE_COOKIE(p))->accum;
181 }
182
183 static int &idx(Node *p) noexcept
184 {
185 return static_cast<Ni *>(NODE_COOKIE(p))->idx;
186 }
187
188 // Checked addition to prevent integer overflow
190 {
191 if constexpr (std::is_integral_v<Distance_Type>)
192 {
193 // Check for positive overflow
194 ah_overflow_error_if(b > 0 && a > std::numeric_limits<Distance_Type>::max() - b)
195 << "Integer overflow in distance addition: " << a << " + " << b;
196
197 // Check for negative overflow (underflow)
198 ah_overflow_error_if(b < 0 && a < std::numeric_limits<Distance_Type>::min() - b)
199 << "Integer underflow in distance addition: " << a << " + " << b;
200 }
201
202 return a + b;
203 }
204
206 GT &g_; // Non-const: algorithm modifies node/arc bits and cookies
208 bool painted_ = false;
209 Node *s_ = nullptr;
210 SA sa_;
212
214 void init_simple(Node *start)
215 {
216 Init_Guard guard([this]()
217 {
218 uninit<Sni>();
219 });
220
221 typename GT::Node_Iterator it(g_);
222 for (int i = 0; it.has_curr(); ++i, it.next_ne())
223 {
224 auto p = it.get_curr();
225 g_.reset_bit(p, Aleph::Spanning_Tree); // set bit to zero
226 NODE_COOKIE(p) = nullptr; // clear stale pointer before allocating
227 auto ptr = new Sni;
228 ptr->accum = Inf;
229 NODE_BITS(p).set_bit(Spanning_Tree, false);
230 NODE_COOKIE(p) = ptr;
231 }
232 s_ = start;
233 accum(s_) = 0;
234 g_.reset_arcs();
235
236 guard.release(); // Successful initialization, prevent cleanup
237 }
238
241 {
242 Init_Guard guard([this]()
243 {
244 uninit<Ni>();
245 arcs_.cut();
246 });
247
248 const size_t n = g_.get_num_nodes();
249 arcs_.cut(); // Clear any previous data
250 arcs_.reserve(n);
251
252 typename GT::Node_Iterator it(g_);
253 for (size_t i = 0; it.has_curr(); ++i, it.next())
254 {
255 // Use touch() to ensure memory is allocated for index i
256 arcs_.touch(i) = nullptr;
257 auto p = it.get_curr();
258 g_.reset_bit(p, Aleph::Spanning_Tree); // set bit to zero
259 NODE_COOKIE(p) = nullptr; // clear stale pointer before allocating
260 auto ptr = new Ni;
261 ptr->accum = Inf;
262 ptr->idx = static_cast<int>(i);
263 NODE_BITS(p).set_bit(Spanning_Tree, false);
264 NODE_BITS(p).set_bit(Depth_First, false); // indicates if it is in queue
265 NODE_COOKIE(p) = ptr;
266 }
267
268 painted_ = false;
269 s_ = start;
270 accum(s_) = 0;
271
272 g_.reset_arcs();
273
274 guard.release(); // Successful initialization, prevent cleanup
275 }
276
278 template <class Info_Type>
279 void uninit()
280 {
281 for (typename GT::Node_Iterator it(g_); it.has_curr(); it.next())
282 {
283 auto p = it.get_curr();
284 delete static_cast<Info_Type *>(NODE_COOKIE(p));
285 NODE_COOKIE(p) = nullptr;
286 }
287 }
288
301 {
302 if (s_ == nullptr)
303 return false;
304
305 size_t num_painted_arcs = 0;
306 size_t num_painted_nodes = 0;
307
308 // Count painted arcs
309 for (Ait<GT, SA> it(g_, sa_); it.has_curr(); it.next_ne())
310 if (IS_ARC_VISITED(it.get_curr(), Aleph::Spanning_Tree))
312
313 // Count painted nodes and verify each has exactly one incoming painted arc
314 for (typename GT::Node_Iterator it(g_); it.has_curr(); it.next_ne())
315 {
316 auto node = it.get_curr();
318 continue;
319
321
322 // Skip root node - it should have no incoming painted arcs
323 if (node == s_)
324 continue;
325
326 // Count incoming painted arcs to this node
327 size_t incoming_count = 0;
328 for (Ait<GT, SA> ait(g_, sa_); ait.has_curr(); ait.next_ne())
329 if (auto arc = ait.get_curr();
332
333 // Each non-root painted node must have exactly 1 incoming painted arc
334 if (incoming_count != 1)
335 return false;
336 }
337
338 // Tree property: #arcs == #nodes - 1
340 }
341
342public:
358 {
359 // empty
360 }
361
380 {
381 if (painted_)
382 {
383 // Clear Spanning_Tree bits from all nodes and arcs
384 for (typename GT::Node_Iterator it(g_); it.has_curr(); it.next_ne())
385 NODE_BITS(it.get_curr()).set_bit(Aleph::Spanning_Tree, false);
386
387 for (Ait<GT, SA> it(g_, sa_); it.has_curr(); it.next_ne())
388 ARC_BITS(it.get_curr()).set_bit(Aleph::Spanning_Tree, false);
389 }
390
391 arcs_.cut();
392 painted_ = false;
393 s_ = nullptr;
394 }
395
399 {
400 return s_ != nullptr;
401 }
402
406 {
407 return painted_;
408 }
409
413 {
414 return s_;
415 }
416
420 {
421 return g_;
422 }
423
424private:
427 {
428 const size_t &n = g_.vsize();
429 if (n <= 1)
430 return; // Nothing to relax for empty or single-node graphs
431
432 for (size_t i = 0; i < n - 1; ++i)
433 for (Ait<GT, SA> it(g_, sa_); it.has_curr(); it.next_ne())
434 {
435 auto arc = it.get_curr();
436 auto src = g_.get_src_node(arc);
437 const auto &accum_src = accum(src);
438 if (accum_src == Inf)
439 continue;
440
441 auto tgt = it.get_tgt_node_ne();
442 auto w = dist_(arc);
443 auto sum = checked_add(accum_src, w);
444 auto &accum_tgt = accum(tgt);
445 if (sum < accum_tgt) // Relax Arc
446 {
447 const auto &index = idx(tgt);
448 arcs_(index) = arc;
449 accum_tgt = sum;
450 }
451 }
452 }
453
456 {
457 if (IS_NODE_VISITED(p, Depth_First)) // is already inside the queue?
458 return;
459 NODE_BITS(p).set_bit(Depth_First, true);
460 q.put(p);
461 }
462
465 {
466 auto ret = q.get();
468 NODE_BITS(ret).set_bit(Depth_First, false);
469 return ret;
470 }
471
474 {
475 for (NAit<GT, SA> it(src, sa_); it.has_curr(); it.next_ne())
476 {
477 auto arc = it.get_curr();
478 auto arc_src = g_.get_src_node(arc);
479 const auto &accum_src = accum(arc_src);
480 if (accum_src == Inf)
481 continue;
482
483 auto tgt = g_.get_tgt_node(arc);
484 auto w = dist_(arc);
485 auto sum = checked_add(accum_src, w);
486 auto &accum_tgt = accum(tgt);
487 if (sum < accum_tgt) // Relax Arc
488 {
489 const auto &index = idx(tgt);
490 arcs_(index) = arc;
491 accum_tgt = sum;
492 put_in_queue(q, tgt);
493 }
494 }
495 }
496
499 { // paint the involved nodes and arcs
500 const size_t n = g_.vsize();
501 for (size_t i = 0; i < n; ++i)
502 {
503 auto arc = arcs_(i);
504 if (arc == nullptr)
505 continue;
506
507 ARC_BITS(arc).set_bit(Aleph::Spanning_Tree, true);
508 auto src = g_.get_src_node(arc);
509 auto tgt = g_.get_tgt_node(arc);
510 NODE_BITS(src).set_bit(Aleph::Spanning_Tree, true);
511 NODE_BITS(tgt).set_bit(Aleph::Spanning_Tree, true);
512 }
513 NODE_BITS(s_).set_bit(Aleph::Spanning_Tree, true);
514
516
517 painted_ = true;
518 }
519
523 {
524 bool negative_cycle = false;
525 for (Ait<GT, SA> it(g_, sa_); it.has_curr(); it.next_ne())
526 {
527 auto arc = it.get_curr();
528 auto src = g_.get_src_node(arc);
529 auto &accum_src = accum(src);
530 if (accum_src == Inf)
531 continue;
532
533 auto tgt = g_.get_tgt_node(arc);
534 auto d = dist_(arc);
535 auto &accum_tgt = accum(tgt);
536 auto sum = checked_add(accum_src, d);
537 if (sum < accum_tgt)
538 {
539 negative_cycle = true;
540 const auto &index = idx(tgt);
541 arcs_(index) = arc;
542 accum_tgt = sum;
543 }
544 }
545 return negative_cycle;
546 }
547
550 {
551 for (Ait<GT, SA> it(g_, sa_); it.has_curr(); it.next_ne())
552 {
553 auto arc = it.get_curr();
554 auto src = g_.get_src_node(arc);
555 auto &accum_src = accum(src);
556 if (accum_src == Inf)
557 continue;
558
559 auto tgt = g_.get_tgt_node(arc);
560 auto d = dist_(arc);
561 auto &accum_tgt = accum(tgt);
562 auto sum = checked_add(accum_src, d);
563 if (sum < accum_tgt)
564 return true;
565 }
566 return false;
567 }
568
570 void link_cookies_and_free(typename GT::Node *start) noexcept
571 {
572 uninit<Ni>();
573
574 // Construct the inverted paths to the start origin node
575 const size_t n = g_.vsize();
576 for (size_t i = 0; i < n; ++i)
577 {
578 auto arc = arcs_(i);
579 if (arc == nullptr)
580 continue;
581
582 auto tgt = g_.get_tgt_node(arc);
583 NODE_COOKIE(tgt) = g_.get_src_node(arc);
584 }
585
586 NODE_COOKIE(start) = nullptr; // just in case there is a negative cycle
587 }
588
589public:
600 {
601 ah_domain_error_if(start == nullptr) << "start node cannot be null";
602
603 init_with_indexes(start);
604
605 relax_arcs();
606 const bool negative_cycle = last_relax_and_prepare_check_negative_cycle();
607
608 // Only paint the tree if there's no negative cycle
609 // A negative cycle makes the shortest path tree meaningless
610 if (not negative_cycle)
611 paint_tree();
612
614
615 return negative_cycle;
616 }
617
631 {
632 ah_domain_error_if(start == nullptr) << "start node cannot be null";
633
634 init_with_indexes(start);
635
636 const auto &n = g_.get_num_nodes();
637
639
641 Node *sentinel = &__sentinel;
642
643 put_in_queue(q, s_);
644 put_in_queue(q, sentinel);
645
646 for (size_t i = 0; not q.is_empty();)
647 {
648 auto src = get_from_queue(q);
649 if (src == sentinel) // Is the sentinel removed?
650 {
651 if (i++ > n)
652 {
653 while (not q.is_empty())
654 get_from_queue(q); // clear Depth_First bits on remaining nodes
655 break;
656 }
657
658 put_in_queue(q, sentinel);
659 }
660 else
661 relax_arcs(src, q);
662 }
663
664 const bool negative_cycle = last_relax_and_prepare_check_negative_cycle();
665
666 // Only paint the tree if there's no negative cycle
667 // A negative cycle makes the shortest path tree meaningless
668 if (not negative_cycle)
669 paint_tree();
670
672
673 return negative_cycle;
674 }
675
676private:
680 {
681 // RAII guard to ensure cleanup on exception
682 struct Dummy_Guard
683 {
684 GT &graph;
685 Node *dummy;
686 std::vector<Arc *> inserted_arcs;
687 bool released = false;
688
689 explicit Dummy_Guard(GT &g, Node *d) : graph(g), dummy(d)
690 {
691 inserted_arcs.reserve(g.get_num_nodes());
692 }
693
695 {
696 if (not released)
697 {
698 // Remove all inserted arcs
699 for (auto arc : inserted_arcs)
700 graph.remove_arc(arc);
701
702 // Remove dummy node
703 if (dummy != nullptr)
704 graph.remove_node(dummy);
705 }
706 }
707
708 void add_arc(Arc *a)
709 {
710 inserted_arcs.push_back(a);
711 }
712 void release()
713 {
714 released = true;
715 }
716
717 Dummy_Guard(const Dummy_Guard &) = delete;
718
719 Dummy_Guard &operator=(const Dummy_Guard &) = delete;
720 };
721
722 s_ = g_.insert_node(typename GT::Node_Type());
724
725 for (typename GT::Node_Iterator it(g_); it.has_curr(); it.next_ne())
726 {
727 auto p = it.get_curr();
728 if (p == s_)
729 continue;
730 auto a = g_.insert_arc(s_, p);
731 guard.add_arc(a);
733 }
734
735 guard.release(); // Successful creation, prevent cleanup
736 return s_;
737 }
738
740 template <class Info_Type>
742 {
743 delete static_cast<Info_Type *>(NODE_COOKIE(p));
744 NODE_COOKIE(p) = nullptr;
745 if (p == s_)
746 s_ = nullptr;
747 g_.remove_node(p);
748 }
749
750public:
758 {
759 ah_domain_error_if(start == nullptr) << "start node cannot be null";
760
761 init_with_indexes(start);
762 // Note: s and accum(s) already set by init_with_indexes
763
764 relax_arcs();
765 const bool negative_cycle = last_relax_and_test_negative_cycle();
766 uninit<Ni>();
767
768 return negative_cycle;
769 }
770
782 {
784
785 // RAII guard ensures dummy node removal even on exception
786 struct Global_Cycle_Guard
787 {
790 bool released = false;
791
793
795 {
796 if (not released and dummy_node != nullptr)
797 {
798 // Clean up: remove dummy node and its arcs
799 bf.remove_dummy_node<Ni>(dummy_node);
800 }
801 }
802
803 void release()
804 {
805 released = true;
806 }
807 };
808
810
812 guard.release();
814
815 return ret;
816 }
817
818private:
821 {
823
824 // we map because Tarjan algorithm modifies cookies
826 for (typename GT::Node_Iterator it(aux); it.has_curr(); it.next_ne())
827 {
828 auto p = it.get_curr();
829 table.insert(p, static_cast<Node *>(NODE_COOKIE(p)));
830 }
831
832 // Save and restore cookies around Tarjan call (Tarjan modifies cookies)
833 Cookie_Saver<GT> cookie_saver(aux, true, false); // only save node cookies
834
835 // Clear cookies for Tarjan's use
836 for (typename GT::Node_Iterator it(aux); it.has_curr(); it.next_ne())
837 NODE_COOKIE(it.get_curr()) = nullptr;
838
839 if (Path<GT> path(aux); Tarjan_Connected_Components<GT, NAit, SA>(sa_).compute_cycle(aux, path))
840 {
841 Path<GT> ret(g_);
842 for (typename Path<GT>::Iterator it(path); it.has_current_node(); it.next_ne())
843 ret.append_directed(static_cast<Node *>(table.find(it.get_current_node_ne())));
844 return ret;
845 }
846
847 return Path<GT>(g_);
848 }
849
850public:
863 {
864 ah_domain_error_if(start == nullptr) << "start node cannot be null";
865
866 init_with_indexes(start);
867
868 relax_arcs();
869 const bool negative_cycle = last_relax_and_prepare_check_negative_cycle();
870 if (not negative_cycle)
871 {
873 return Path<GT>(g_);
874 }
875
877 if (ret.is_empty())
878 WARNING(
879 "Serious inconsistency. Bellman-Ford algorithm has detected\n"
880 "a negative cycle, but Tarjan algorithm executed on partial\n"
881 "graph has not found such cycle\n\n"
882 "Be very careful, this is provably a bug");
883
885 return ret;
886 }
887
894 {
895 auto start = create_dummy_node();
896
897 // RAII guard ensures dummy node removal even on exception
898 Init_Guard guard([this, start]()
899 {
901 });
902
903 auto ret_val = test_negative_cycle(start);
904 guard.release();
906 return ret_val;
907 }
908
952 std::tuple<Path<GT>, size_t> search_negative_cycle(Node *start, double it_factor, const size_t step)
953 {
954 ah_domain_error_if(start == nullptr) << "start node cannot be null";
955
956 init_with_indexes(start);
957
958 const auto &n = g_.get_num_nodes();
961 Node *sentinel = &__sentinel;
962 put_in_queue(q, s_);
963 put_in_queue(q, sentinel);
964
965 double threshold = it_factor * n;
966 Path<GT> ret(g_);
967
968 size_t i = 0;
969 while (not q.is_empty())
970 {
971 auto src = get_from_queue(q);
972 if (src == sentinel)
973 {
974 if (i++ > n)
975 break;
976
977 put_in_queue(q, sentinel);
978 if (i >= threshold) // must I search negative cycles?
979 {
981 if (not ret.is_empty()) // negative cycle found?
982 {
984 return std::make_tuple(std::forward<Path<GT>>(ret), i);
985 }
986 threshold += step;
987 }
988 }
989 else
990 relax_arcs(src, q);
991 }
992
993 if (const bool negative_cycle = last_relax_and_prepare_check_negative_cycle())
994 {
996 if (ret.is_empty())
997 WARNING(
998 "Serious inconsistency. Bellman-Ford algorithm has detected\n"
999 "a negative cycle, but Tarjan algorithm executed on partial\n"
1000 "graph has not found such cycle\n\n"
1001 "Be very careful, this provably is a bug");
1002 }
1003
1005 return std::make_tuple(std::forward<Path<GT>>(ret), i);
1006 }
1007
1016 {
1017 ah_domain_error_if(start == nullptr) << "start node cannot be null";
1018
1019 init_with_indexes(start);
1020
1021 const auto &n = g_.get_num_nodes();
1024 Node *sentinel = &__sentinel;
1025 put_in_queue(q, s_);
1026 put_in_queue(q, sentinel);
1027
1028 Path<GT> ret(g_);
1029 for (size_t i = 0; not q.is_empty(); /* nothing */)
1030 {
1031 auto src = get_from_queue(q);
1032 if (src == sentinel)
1033 {
1034 if (i++ > n)
1035 break;
1036
1037 put_in_queue(q, sentinel);
1038 }
1039 else
1040 relax_arcs(src, q);
1041 }
1042
1043 if (const bool negative_cycle = last_relax_and_prepare_check_negative_cycle())
1044 {
1046 if (ret.is_empty())
1047 WARNING(
1048 "Serious inconsistency. Bellman-Ford algorithm has detected\n"
1049 "a negative cycle, but Tarjan algorithm executed on partial\n"
1050 "graph has not found such cycle\n\n"
1051 "Be very careful, this provably is a bug");
1052 }
1053
1055
1056 return ret;
1057 }
1058
1069 std::tuple<Path<GT>, size_t> search_negative_cycle(double it_factor, const size_t step)
1070 {
1071 auto start = create_dummy_node();
1072
1073 // RAII guard ensures dummy node removal even on exception
1074 Init_Guard guard([this, start]()
1075 {
1076 remove_dummy_node<Ni>(start);
1077 });
1078
1079 auto ret_val = search_negative_cycle(start, it_factor, step);
1080 guard.release();
1081 remove_dummy_node<Ni>(start);
1082 return ret_val;
1083 }
1084
1089 {
1090 auto start = create_dummy_node();
1091
1092 // RAII guard ensures dummy node removal even on exception
1093 Init_Guard guard([this, start]()
1094 {
1095 remove_dummy_node<Ni>(start);
1096 });
1097
1098 auto ret_val = search_negative_cycle(start);
1099 guard.release();
1100 remove_dummy_node<Ni>(start);
1101 return ret_val;
1102 }
1103
1114 {
1115 ah_domain_error_if(not painted_) << "Spanning tree has not been painted";
1116
1117 return arcs_;
1118 }
1119
1131 {
1132 ah_domain_error_if(not painted_) << "Graph has not been previously painted";
1133
1134 ah_domain_error_if(node == nullptr) << "node cannot be null";
1135
1136 // Follow predecessor chain to verify reachability
1137 auto curr = node;
1138 while (curr != s_)
1139 {
1140 auto parent = static_cast<Node *>(NODE_COOKIE(curr));
1141 ah_domain_error_if(parent == nullptr) << "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();
1155 g_.get_tgt_node(arc) == curr && IS_ARC_VISITED(arc, Aleph::Spanning_Tree))
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 {
1176 ah_domain_error_if(not painted_ and with_map) << "Spanning tree has not been painted";
1177
1178 clear_graph(tree);
1179
1181 for (typename GT::Node_Iterator it(g_); it.has_curr(); it.next_ne())
1182 {
1183 auto gp = it.get_curr();
1184 auto tp = tree.insert_node(gp->get_info());
1185 table.insert(gp, tp);
1186 }
1187
1188 for (typename GT::Node_Iterator it(g_); it.has_curr(); it.next_ne())
1189 {
1190 auto gtgt = it.get_curr();
1191 auto gsrc = static_cast<Node *>(NODE_COOKIE(gtgt));
1192 if (gsrc == nullptr)
1193 continue; // This is the source node of the spanning tree
1194
1195 // Find the spanning tree arc from gsrc to gtgt
1196 Arc *garc = nullptr;
1197 for (Out_Iterator<GT, SA> ait(gsrc, sa_); ait.has_curr(); ait.next_ne())
1198 {
1199 auto a = ait.get_curr();
1201 {
1202 garc = a;
1203 break;
1204 }
1205 }
1206 ah_logic_error_unless(garc != nullptr) << "Arc not found between nodes in spanning tree";
1207
1208 auto tsrc_ptr = table.search(gsrc);
1209 auto ttgt_ptr = table.search(gtgt);
1210 ah_logic_error_unless(tsrc_ptr) << "Source node not found in mapping table";
1211 ah_logic_error_unless(ttgt_ptr) << "Target node not found in mapping table";
1212 auto tarc = tree.insert_arc(tsrc_ptr->second, ttgt_ptr->second, garc->get_info());
1213 if (with_map)
1215 }
1216
1217 if (with_map)
1218 table.for_each([](const auto &p)
1219 {
1220 GT::map_nodes(p.first, p.second);
1221 });
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
1351template <AlephGraph GT,
1353 template <class, class> class Ait = Arc_Iterator,
1354 template <class, class> class NAit = Node_Arc_Iterator,
1357{
1368 bool operator()(GT &g, Path<GT> &path, Distance &d, SA &sa) const
1369 {
1370 return Bellman_Ford<GT, Distance, Ait, NAit, SA>(g, d, sa).test_negative_cycle(path);
1371 }
1372
1373 bool operator()(GT &g, Path<GT> &path, Distance &&d = Distance(), SA &&sa = SA()) const
1374 {
1375 return Bellman_Ford<GT, Distance, Ait, NAit, SA>(g, d, sa).test_negative_cycle(path);
1376 }
1377
1389 bool operator()(GT &g, typename GT::Node *s, Path<GT> &path, Distance &d, SA &sa) const
1390 {
1391 return Bellman_Ford<GT, Distance, Ait, NAit, SA>(g, d, sa).test_negative_cycle(s, path);
1392 }
1393
1395 typename GT::Node *s,
1396 Path<GT> &path,
1397 Distance &&d = Distance(),
1398 SA &&sa = SA()) const
1399 {
1400 return Bellman_Ford<GT, Distance, Ait, NAit, SA>(g, d, sa).test_negative_cycle(s, path);
1401 }
1402
1411 Path<GT> operator()(GT &g, typename GT::Node *s, Distance &d, SA &sa) const
1412 {
1413 return Bellman_Ford<GT, Distance, Ait, NAit, SA>(g, d, sa).search_negative_cycle(s);
1414 }
1415
1417 Path<GT> operator()(GT &g, typename GT::Node *s, Distance &&d = Distance(), SA &&sa = SA()) const
1418 {
1419 return Bellman_Ford<GT, Distance, Ait, NAit, SA>(g, d, sa).search_negative_cycle(s);
1420 }
1421
1429 Path<GT> operator()(GT &g, Distance &d, SA &sa) const
1430 {
1431 return Bellman_Ford<GT, Distance, Ait, NAit, SA>(g, d, sa).search_negative_cycle();
1432 }
1433
1435 Path<GT> operator()(GT &g, Distance &&d = Distance(), SA &&sa = SA()) const
1436 {
1437 return Bellman_Ford<GT, Distance, Ait, NAit, SA>(g, d, sa).search_negative_cycle();
1438 }
1439};
1440} // end namespace Aleph
1441
1442#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:314
#define ah_overflow_error_if(C)
Throws std::overflow_error if condition holds.
Definition ah-errors.H:468
#define ah_domain_error_if(C)
Throws std::domain_error if condition holds.
Definition ah-errors.H:527
C++20 concepts for the protocol shared by graph algorithms.
#define WARNING(...)
Print a warning message (no-op when MESSAGES is not defined).
Definition ahDefs.H:262
RAII guard for exception-safe resource cleanup.
List_Graph< Graph_Node< Node_Info >, Graph_Arc< Arc_Info > > GT
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.
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.
DynArray< typename GT::Arc * > arcs_
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.
bool has_curr() const noexcept
Return true the iterator has an current arc.
Definition tpl_graph.H:1726
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.
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.
RAII guard for graph algorithm initialization.
virtual Node * insert_node(Node *node) noexcept
Insertion of a node already allocated.
Definition tpl_graph.H:525
virtual void remove_node(Node *node) noexcept
Remove a node from the graph and free its memory.
Definition tpl_graph.H:544
typename Node::Node_Type Node_Type
The arc class type.
Definition tpl_graph.H:437
virtual void remove_arc(Arc *arc) noexcept
Remove an arc from the graph and free it.
Definition tpl_graph.H:650
Arc * insert_arc(Node *src_node, Node *tgt_node, void *a)
Definition tpl_graph.H:605
Filtered iterator for outcoming arcs of a node.
Definition tpl_graph.H:1831
Iterator on nodes and arcs of a path.
Definition tpl_graph.H:3311
bool has_current_node() const noexcept
Return true if the iterator has a current node.
Definition tpl_graph.H:3424
Path on a graph.
Definition tpl_graph.H:2772
void for_each(Operation &operation)
Traverse all the container and performs an operation on each element.
Definition ah-dry.H:796
void reset_arcs() const
Reset all the arcs of graph (the control bits, the state, the counter and the cookie)
Definition graph-dry.H:975
Node * get_src_node(Arc *arc) const noexcept
Return the source node of arc (only for directed graphs)
Definition graph-dry.H:779
constexpr size_t get_num_nodes() const noexcept
Return the total of nodes of graph.
Definition graph-dry.H:737
static void map_arcs(A1 *p, A2 *q) noexcept
Map the arcs through their cookies.
Definition graph-dry.H:1074
void reset_bit(Node *node, int bit) const noexcept
Reset the bit of node (to zero)
Definition graph-dry.H:849
constexpr size_t vsize() const noexcept
Definition graph-dry.H:746
auto get_node_it() const noexcept
Obtains an iterator to the nodes of graph.
Definition graph-dry.H:2886
Node * get_tgt_node(Arc *arc) const noexcept
Return the target node of arc (only for directed graphs)
Definition graph-dry.H:785
static void map_nodes(N1 *p, N2 *q) noexcept
Map the nodes through their cookies.
Definition graph-dry.H:1043
RAII guards for graph node/arc cookies.
__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:4121
#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:3659
size_t blossom_maximum_cardinality_matching(const GT &g, DynDlist< typename GT::Arc * > &matching, SA sa=SA())
Alias of compute_maximum_cardinality_general_matching().
Definition Blossom.H:466
#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
and
Check uniqueness with explicit hash + equality functors.
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:1165
Detects if a negative cycle exists and eventually computes it.
Path< GT > operator()(GT &g, typename GT::Node *s, Distance &d, SA &sa) const
Search for a negative cycle starting from a specific node.
bool operator()(GT &g, typename GT::Node *s, Path< GT > &path, Distance &&d=Distance(), SA &&sa=SA()) const
Path< GT > operator()(GT &g, Distance &&d=Distance(), SA &&sa=SA()) const
This is an overloaded member function, provided for convenience. It differs from the above function o...
bool operator()(GT &g, Path< GT > &path, Distance &d, SA &sa) const
Invokes the detection and calculation of a negative cycle.
bool operator()(GT &g, typename GT::Node *s, Path< GT > &path, Distance &d, SA &sa) const
Invokes the detection and calculation of a negative cycle.
Path< GT > operator()(GT &g, Distance &d, SA &sa) const
Search for a negative cycle in the entire graph.
Path< GT > operator()(GT &g, typename GT::Node *s, Distance &&d=Distance(), SA &&sa=SA()) const
This is an overloaded member function, provided for convenience. It differs from the above function o...
bool operator()(GT &g, Path< GT > &path, Distance &&d=Distance(), SA &&sa=SA()) const
Default filter for filtered iterators on arcs.
Definition tpl_graph.H:1001
Filtered iterator of adjacent arcs of a node.
Definition tpl_graph.H:1120
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.