Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
Zero_One_BFS.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
83# ifndef ZERO_ONE_BFS_H
84# define ZERO_ONE_BFS_H
85
86# include <limits>
87# include <type_traits>
88# include <tpl_dynDlist.H>
89# include <tpl_graph_utils.H>
90# include <ah-errors.H>
91
92namespace Aleph
93{
94
125template <class GT,
126 class Distance = Dft_Dist<GT>,
127 template <typename, class> class Itor = Node_Arc_Iterator,
128 class SA = Dft_Show_Arc<GT>>
130{
131public:
133 using Node = typename GT::Node;
134 using Arc = typename GT::Arc;
135
136private:
139 bool painted = false;
140 GT * ptr_g = nullptr;
141 Node * s = nullptr;
142
143 static constexpr Distance_Type Inf =
144 std::numeric_limits<Distance_Type>::max();
145
147 {
151
152 Node_Info() noexcept : dist(Inf), parent(nullptr), parent_arc(nullptr) {}
153 };
154
161
164
165#define ZOB_INFO(p) (static_cast<Node_Info *>(NODE_COOKIE(p)))
166#define ZOB_PNTD(p) (static_cast<Painted_Info *>(NODE_COOKIE(p)))
167#define ZOB_DIST(p) (ZOB_INFO(p)->dist)
168#define ZOB_PARENT(p) (ZOB_INFO(p)->parent)
169#define ZOB_PARENT_ARC(p) (ZOB_INFO(p)->parent_arc)
170
172 {
173 for (auto it = owned_node_infos.get_it(); it.has_curr(); it.next())
174 {
175 auto item = it.get_curr();
176 auto p = item.first;
177 auto info = item.second;
178 if (info != nullptr)
179 delete info;
180 if (NODE_COOKIE(p) == info)
181 NODE_COOKIE(p) = nullptr;
182 }
183 owned_node_infos.empty();
184 }
185
187 {
188 for (auto it = owned_painted_infos.get_it(); it.has_curr(); it.next())
189 {
190 auto item = it.get_curr();
191 auto p = item.first;
192 auto info = item.second;
193 if (info != nullptr)
194 delete info;
195 if (NODE_COOKIE(p) == info)
196 NODE_COOKIE(p) = nullptr;
197 }
198 owned_painted_infos.empty();
199 }
200
202 {
203 painted = false;
204 ptr_g = nullptr;
205 s = nullptr;
206 }
207
210 {
211 Zero_One_BFS * owner = nullptr;
212 const GT * g = nullptr;
213 bool committed = false;
214
215 public:
221 : owner(&__owner), g(&__g)
222 {
223 // empty
224 }
225
234
236 void commit() noexcept { committed = true; }
237
240 {
241 if (not committed)
242 {
245 }
246 }
247 };
248
249 void init(const GT & g)
250 {
251 ptr_g = &const_cast<GT &>(g);
254 for (typename GT::Node_Iterator it(g); it.has_curr(); it.next())
255 {
256 auto p = it.get_curr();
258 NODE_COOKIE(p) = nullptr;
259 }
260 for (typename GT::Arc_Iterator it(g); it.has_curr(); it.next())
261 g.reset_bit(it.get_curr(), Aleph::Spanning_Tree);
262
263 try
264 {
265 for (typename GT::Node_Iterator it(g); it.has_curr(); it.next())
266 {
267 auto p = it.get_curr();
268 auto info = new Node_Info;
269 NODE_COOKIE(p) = info;
270 try
271 {
272 owned_node_infos.append({p, info});
273 }
274 catch (...)
275 {
276 delete info;
277 NODE_COOKIE(p) = nullptr;
278 throw;
279 }
280 }
281 }
282 catch (...)
283 {
286 throw;
287 }
288 }
289
291 {
292 // Two-phase conversion to be exception-safe:
293 // 1. Allocate all Painted_Info objects
294 // 2. Atomically swap and delete old Node_Info
296 try
297 {
298 for (typename GT::Node_Iterator it(*ptr_g); it.has_curr(); it.next())
299 {
300 auto p = it.get_curr();
301 auto info = ZOB_INFO(p);
302 pending.append({p, new Painted_Info{info->parent, info->dist}});
303 }
304 }
305 catch (...)
306 {
307 for (auto it = pending.get_it(); it.has_curr(); it.next())
308 delete it.get_curr().second;
309 throw;
310 }
311
312 // Phase 2: Apply changes
314 for (auto it = pending.get_it(); it.has_curr(); it.next())
315 {
316 auto item = it.get_curr();
317 auto p = item.first;
318 auto pinfo = item.second;
319 NODE_COOKIE(p) = pinfo;
320 }
321 owned_painted_infos.swap(pending);
322 }
323
325 {
326 if (ptr_g == nullptr)
327 return;
328
331
332 for (typename GT::Node_Iterator it(*ptr_g); it.has_curr(); it.next())
333 {
334 auto p = it.get_curr();
336 }
337
338 for (typename GT::Arc_Iterator it(*ptr_g); it.has_curr(); it.next())
339 ptr_g->reset_bit(it.get_curr(), Aleph::Spanning_Tree);
340 }
341
342 void run_bfs(const GT & g, Node * start, Node * end = nullptr)
343 {
344 ZOB_DIST(start) = 0;
345 NODE_BITS(start).set_bit(Aleph::Spanning_Tree, true);
346
348 deque.append(start);
349
350 while (not deque.is_empty())
351 {
352 auto curr = deque.remove_first();
353 auto curr_dist = ZOB_DIST(curr);
354
355 if (end != nullptr and curr == end)
356 break;
357
358 for (Itor<GT, SA> it(curr, sa); it.has_curr(); it.next())
359 {
360 auto arc = it.get_current_arc();
361 auto tgt = g.get_connected_node(arc, curr);
362
363 auto w = distance(arc);
365 << "Zero_One_BFS: arc weight must be 0 or 1, got " << w;
366
367 auto new_dist = curr_dist + w;
368
369 if (new_dist < ZOB_DIST(tgt))
370 {
372 if (prev_parent_arc != nullptr)
374
375 ZOB_DIST(tgt) = new_dist;
376 ZOB_PARENT(tgt) = curr;
377 ZOB_PARENT_ARC(tgt) = arc;
378 NODE_BITS(tgt).set_bit(Aleph::Spanning_Tree, true);
379
380 // Mark the selected parent arc in the spanning tree
381 ARC_BITS(arc).set_bit(Aleph::Spanning_Tree, true);
382
383 if (w == Distance_Type(0))
384 deque.insert(tgt); // push front
385 else
386 deque.append(tgt); // push back
387 }
388 }
389 }
390 }
391
392public:
399 : sa(__sa), distance(dist)
400 {
401 // empty
402 }
403
414
419
424
429
444 void paint_min_paths_tree(const GT & g, Node * start)
445 {
446 ah_domain_error_if(start == nullptr) << "start node cannot be null";
447 ah_domain_error_if(g.get_num_nodes() == 0) << "graph is empty";
448
449 if (ptr_g != nullptr)
451
452 painted = false;
453 s = nullptr;
454
455 init(g);
456 ZOB_Init_Guard guard(*this, g);
457 s = start;
458 run_bfs(g, start);
459 uninit_paint();
460 guard.commit();
461 painted = true;
462 }
463
474 {
475 ah_domain_error_if(not painted) << "Graph has not been painted";
476 ah_domain_error_if(node == nullptr) << "node cannot be null";
477
478 if (node == s)
479 return Distance_Type(0);
480
482 << "node is not reachable from start";
483
484 // O(1) access thanks to Painted_Info
485 auto dist_val = ZOB_PNTD(node)->dist;
486 if (dist_val != Inf)
487 return dist_val;
488
489 // Fallback: Compute distance by walking the path
491 for (auto curr = node; curr != s;)
492 {
493 auto pinfo = ZOB_PNTD(curr);
494 auto parent = pinfo->parent;
495 if (parent == nullptr)
496 break;
497
498 for (Itor<GT, SA> it(parent, sa); it.has_curr(); it.next())
499 {
500 auto arc = it.get_current_arc();
501 auto tgt = ptr_g->get_connected_node(arc, parent);
502 if (tgt == curr and IS_ARC_VISITED(arc, Aleph::Spanning_Tree))
503 {
504 total += distance(arc);
505 break;
506 }
507 }
508 curr = parent;
509 }
510
511 return total;
512 }
513
524 {
525 ah_domain_error_if(not painted) << "Graph has not been painted";
526 ah_domain_error_if(ptr_g == nullptr) << "No computation has been done";
527 ah_domain_error_if(end == nullptr) << "end pointer is null";
528
529 path.empty();
530 if (end == s)
531 {
532 path.set_graph(*ptr_g, s);
533 return 0;
534 }
535
537 return Inf;
538
540 path.set_graph(*ptr_g, s);
542 for (auto curr = end; curr != s;)
543 {
544 auto parent = ZOB_PNTD(curr)->parent;
545 if (parent == nullptr)
546 return Inf;
547
548 bool found = false;
549 for (Itor<GT, SA> it(parent, sa); it.has_curr(); it.next())
550 {
551 auto arc = it.get_current_arc();
552 if (ptr_g->get_connected_node(arc, parent) == curr and
554 {
555 arcs.insert(arc);
556 total += distance(arc);
557 curr = parent;
558 found = true;
559 break;
560 }
561 }
562 if (not found)
563 return Inf;
564 }
565
566 for (auto it = arcs.get_it(); it.has_curr(); it.next())
567 path.append(it.get_curr());
568
569 return total;
570 }
571
589 Distance_Type find_min_path(const GT & g, Node * start, Node * end,
590 Path<GT> & path)
591 {
592 ah_domain_error_if(start == nullptr) << "start node cannot be null";
593 ah_domain_error_if(end == nullptr) << "end node cannot be null";
594 ah_domain_error_if(g.get_num_nodes() == 0) << "graph is empty";
595
596 if (ptr_g != nullptr)
598
599 path.empty();
600
601 painted = false;
602 s = nullptr;
603
604 init(g);
605 ZOB_Init_Guard guard(*this, g);
606 s = start;
607 run_bfs(g, start, end);
608 uninit_paint();
609 guard.commit();
610 painted = true;
611
613 return Inf;
614
615 return get_min_path(end, path);
616 }
617
626 Distance_Type operator()(const GT & g, Node * start, Node * end,
627 Path<GT> & path)
628 {
629 return find_min_path(g, start, end, path);
630 }
631
632#undef ZOB_INFO
633#undef ZOB_PNTD
634#undef ZOB_DIST
635#undef ZOB_PARENT
636#undef ZOB_PARENT_ARC
637};
638
639} // end namespace Aleph
640
641# endif // ZERO_ONE_BFS_H
#define ZOB_INFO(p)
#define ZOB_PARENT_ARC(p)
#define ZOB_PARENT(p)
#define ZOB_DIST(p)
#define ZOB_PNTD(p)
Exception handling system with formatted messages for Aleph-w.
#define ah_domain_error_if(C)
Throws std::domain_error if condition holds.
Definition ah-errors.H:522
List_Graph< Graph_Node< Node_Info >, Graph_Arc< Arc_Info > > GT
long double w
Definition btreepic.C:153
Default distance accessor for arc weights.
Dynamic doubly linked list with O(1) size and bidirectional access.
Dynamic singly linked list with functional programming support.
Definition htlist.H:1155
T & insert(const T &item)
Definition htlist.H:1220
T & append(const T &item)
Definition htlist.H:1271
Path on a graph.
Definition tpl_graph.H:2669
void empty()
Clean the path: all the nodes and arc are removed.
Definition tpl_graph.H:2819
void set_graph(const GT &__g, Node *start_node=nullptr)
Set the graph of the path.
Definition tpl_graph.H:2797
void append(Arc *arc)
Append an arc to the path.
Definition tpl_graph.H:2872
RAII guard for Zero_One_BFS initialization and cleanup.
ZOB_Init_Guard(const ZOB_Init_Guard &)=delete
Deleted copy constructor.
void commit() noexcept
Commits the operation, preventing cleanup on destruction.
ZOB_Init_Guard & operator=(const ZOB_Init_Guard &)=delete
Deleted copy assignment.
~ZOB_Init_Guard() noexcept
Destructor.
ZOB_Init_Guard(ZOB_Init_Guard &&)=delete
Deleted move constructor.
ZOB_Init_Guard(Zero_One_BFS &__owner, const GT &__g) noexcept
Constructor.
0-1 BFS algorithm for shortest paths in graphs with 0/1 weights.
void init(const GT &g)
void release_owned_painted_infos() noexcept
~Zero_One_BFS() noexcept
Destructor.
bool is_painted() const noexcept
Check if a computation has been performed.
void release_owned_node_infos() noexcept
static constexpr Distance_Type Inf
GT * get_graph() const noexcept
Get the graph of the last computation.
Distance_Type operator()(const GT &g, Node *start, Node *end, Path< GT > &path)
Computes shortest path (operator interface).
Distance_Type get_min_path(Node *end, Path< GT > &path)
Extracts a shortest path from a previously painted graph.
void run_bfs(const GT &g, Node *start, Node *end=nullptr)
typename Distance::Distance_Type Distance_Type
DynList< std::pair< Node *, Node_Info * > > owned_node_infos
Zero_One_BFS(Distance dist=Distance(), SA __sa=SA())
Constructor.
Distance_Type find_min_path(const GT &g, Node *start, Node *end, Path< GT > &path)
Computes the shortest path from start to end.
typename GT::Node Node
void reset_state_after_failure() noexcept
void paint_min_paths_tree(const GT &g, Node *start)
Paints the shortest paths tree on the graph from start.
Distance_Type get_distance(Node *node)
Gets the accumulated distance to a node after painting.
Node * get_start_node() const noexcept
Get the start node of the last computation.
DynList< std::pair< Node *, Painted_Info * > > owned_painted_infos
typename GT::Arc Arc
constexpr size_t get_num_nodes() const noexcept
Return the total of nodes of graph.
Definition graph-dry.H:695
Node * get_connected_node(Arc *arc, Node *node) const noexcept
Return the adjacent node to node through arc.
Definition graph-dry.H:778
void reset_bit(Node *node, int bit) const noexcept
Reset the bit of node (to zero)
Definition graph-dry.H:807
auto get_it() const
Return a properly initialized iterator positioned at the first item on the container.
Definition ah-dry.H:222
Definition deque.H:48
DynArray< Graph::Arc * > arcs
Definition graphpic.C:408
#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
#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.
@ 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.
Divide_Conquer_DP_Result< Cost > divide_and_conquer_partition_dp(const size_t groups, const size_t n, Transition_Cost_Fn transition_cost, const Cost inf=dp_optimization_detail::default_inf< Cost >())
Optimize partition DP using divide-and-conquer optimization.
static std::atomic< bool > init
Definition hash-fct.C:53
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
Stores parent and distance after painting for O(1) access.
Distance accessor.
Dynamic doubly linked list implementation.
Utility algorithms and operations for graphs.