Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
Bidirectional_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
74# ifndef BIDIRECTIONAL_BFS_H
75# define BIDIRECTIONAL_BFS_H
76
77# include <ah-graph-concepts.H>
78
79# include <limits>
80# include <tpl_dynListQueue.H>
81# include <tpl_graph_utils.H>
82# include <ah-errors.H>
83
84namespace Aleph
85{
86
114template <AlephGraph GT,
115 template <typename, class> class Fwd_Itor = Node_Arc_Iterator,
116 template <typename, class> class Bck_Itor = Node_Arc_Iterator,
119{
120public:
121 using Node = typename GT::Node;
122 using Arc = typename GT::Arc;
123
124private:
125 SA sa;
126
127 // Direction constants
128 static constexpr int UNSEEN = 0;
129 static constexpr int FORWARD = 1;
130 static constexpr int BACKWARD = 2;
131 static constexpr size_t INF_DIST = std::numeric_limits<size_t>::max();
132
133 struct BFS_Info
134 {
135 Node * fwd_parent = nullptr;
136 Node * bck_parent = nullptr;
137 Arc * fwd_parent_arc = nullptr;
138 Arc * bck_parent_arc = nullptr;
141 int dir = UNSEEN;
142 };
143
144#define BIBFS_INFO(p) (static_cast<BFS_Info *>(NODE_COOKIE(p)))
145#define BIBFS_DIR(p) (BIBFS_INFO(p)->dir)
146#define BIBFS_FWD_PARENT(p) (BIBFS_INFO(p)->fwd_parent)
147#define BIBFS_BCK_PARENT(p) (BIBFS_INFO(p)->bck_parent)
148#define BIBFS_FWD_PARC(p) (BIBFS_INFO(p)->fwd_parent_arc)
149#define BIBFS_BCK_PARC(p) (BIBFS_INFO(p)->bck_parent_arc)
150#define BIBFS_FWD_DIST(p) (BIBFS_INFO(p)->fwd_dist)
151#define BIBFS_BCK_DIST(p) (BIBFS_INFO(p)->bck_dist)
152
155 {
157 const GT * g = nullptr;
158
159 public:
165 : owner(&__owner), g(&__g)
166 {
167 // empty
168 }
169
178
181 {
182 owner->cleanup(*g);
183 }
184 };
185
186 void init(const GT & g)
187 {
188 for (typename GT::Node_Iterator it(g); it.has_curr(); it.next())
189 NODE_COOKIE(it.get_curr()) = nullptr;
190
191 try
192 {
193 for (typename GT::Node_Iterator it(g); it.has_curr(); it.next())
194 {
195 auto p = it.get_curr();
196 NODE_COOKIE(p) = new BFS_Info;
197 }
198 }
199 catch (...)
200 {
201 cleanup(g);
202 throw;
203 }
204 }
205
206 void cleanup(const GT & g)
207 {
208 for (typename GT::Node_Iterator it(g); it.has_curr(); it.next())
209 {
210 auto p = it.get_curr();
211 delete BIBFS_INFO(p);
212 NODE_COOKIE(p) = nullptr;
213 }
214 }
215
216 [[nodiscard]] bool is_seen(Node * p, const int dir) const noexcept
217 {
218 return (BIBFS_DIR(p) & dir) != 0;
219 }
220
221 void mark_seen(Node * p, const int dir) noexcept
222 {
223 BIBFS_DIR(p) |= dir;
224 }
225
226 [[nodiscard]] size_t get_dist(Node * p, const int dir) const noexcept
227 {
228 return dir == FORWARD ? BIBFS_FWD_DIST(p) : BIBFS_BCK_DIST(p);
229 }
230
231 void set_dist(Node * p, const int dir, const size_t d) noexcept
232 {
233 if (dir == FORWARD)
234 BIBFS_FWD_DIST(p) = d;
235 else
236 BIBFS_BCK_DIST(p) = d;
237 }
238
239 Node *& parent_ref(Node * p, const int dir) noexcept
240 {
241 if (dir == FORWARD)
242 return BIBFS_FWD_PARENT(p);
243 return BIBFS_BCK_PARENT(p);
244 }
245
246 Arc *& parent_arc_ref(Node * p, const int dir) noexcept
247 {
248 if (dir == FORWARD)
249 return BIBFS_FWD_PARC(p);
250 return BIBFS_BCK_PARC(p);
251 }
252
253 template <template <typename, class> class Itor>
255 const int my_dir, const int other_dir,
256 size_t & best_len, Node *& best_meeting)
257 {
259
260 while (not frontier.is_empty())
261 {
262 auto curr = frontier.get();
263 const auto curr_dist = get_dist(curr, my_dir);
264
265 for (Itor<GT, SA> it(curr, sa); it.has_curr(); it.next())
266 {
267 auto arc = it.get_current_arc();
268 auto tgt = g.get_connected_node(arc, curr);
269
270 const bool seen_my = is_seen(tgt, my_dir);
271 if (not seen_my)
272 {
273 mark_seen(tgt, my_dir);
274 parent_ref(tgt, my_dir) = curr;
275 parent_arc_ref(tgt, my_dir) = arc;
276 set_dist(tgt, my_dir, curr_dist + 1);
277 next_frontier.put(tgt);
278 }
279
280 if (is_seen(tgt, other_dir))
281 {
282 const auto my_dist = get_dist(tgt, my_dir);
283 const auto other_dist = get_dist(tgt, other_dir);
284
286 {
287 const auto candidate_len = my_dist + other_dist;
289 {
291 best_meeting = tgt;
292 }
293 }
294 }
295 }
296 }
297
299 }
300
301 void build_path(const GT & g, Node * start, Node * meeting, Node * end,
302 Path<GT> & path)
303 {
304 path.empty();
305
306 if (start == end)
307 {
308 path.set_graph(g, start);
309 return;
310 }
311
312 path.set_graph(g, start);
313
314 // Reconstruct start -> meeting using stored forward parent arcs.
316 for (auto curr = meeting; curr != start; curr = BIBFS_FWD_PARENT(curr))
317 {
318 ah_runtime_error_if(BIBFS_FWD_PARENT(curr) == nullptr)
319 << "Bidirectional_BFS: inconsistent forward parent chain";
320 ah_runtime_error_if(BIBFS_FWD_PARC(curr) == nullptr)
321 << "Bidirectional_BFS: missing forward parent arc";
322 fwd_arcs.insert(BIBFS_FWD_PARC(curr)); // prepend for start->meeting order
323 }
324
325 for (auto it = fwd_arcs.get_it(); it.has_curr(); it.next())
326 path.append(it.get_curr());
327
328 // Reconstruct meeting -> end using stored backward parent arcs.
329 for (auto curr = meeting; curr != end; curr = BIBFS_BCK_PARENT(curr))
330 {
331 ah_runtime_error_if(BIBFS_BCK_PARENT(curr) == nullptr)
332 << "Bidirectional_BFS: inconsistent backward parent chain";
333 ah_runtime_error_if(BIBFS_BCK_PARC(curr) == nullptr)
334 << "Bidirectional_BFS: missing backward parent arc";
335 path.append(BIBFS_BCK_PARC(curr));
336 }
337 }
338
339public:
344 Bidirectional_BFS(SA __sa = SA()) : sa(__sa) {}
345
351
371 bool find_path(const GT & g, Node * start, Node * end, Path<GT> & path)
372 {
373 ah_domain_error_if(start == nullptr) << "start node cannot be null";
374 ah_domain_error_if(end == nullptr) << "end node cannot be null";
375 ah_domain_error_if(g.get_num_nodes() == 0) << "graph is empty";
376
377 path.empty();
378
379 if (start == end)
380 {
381 path.set_graph(g, start);
382 return true;
383 }
384
385 init(g);
386 BiBFS_Init_Guard guard(*this, g);
387
388 bool found = false;
389 Node * meeting = nullptr;
390
391 mark_seen(start, FORWARD);
392 mark_seen(end, BACKWARD);
393 set_dist(start, FORWARD, 0);
394 set_dist(end, BACKWARD, 0);
395
398
399 fwd_frontier.put(start);
400 bck_frontier.put(end);
401
402 size_t fwd_depth = 0;
403 size_t bck_depth = 0;
404 size_t best_len = INF_DIST;
405
406 while (not fwd_frontier.is_empty() and not bck_frontier.is_empty())
407 {
408 const bool expand_forward = fwd_frontier.size() <= bck_frontier.size();
409
410 if (expand_forward)
411 {
414 ++fwd_depth;
415 }
416 else
417 {
420 ++bck_depth;
421 }
422
423 if (meeting != nullptr)
424 {
425 const size_t min_depth = fwd_depth < bck_depth ? fwd_depth : bck_depth;
426 const size_t lower_bound = min_depth + 1;
427 if (lower_bound >= best_len)
428 break;
429 }
430 }
431
432 found = meeting != nullptr;
433 if (found)
434 build_path(g, start, meeting, end, path);
435
436 return found;
437 }
438
447 bool operator()(const GT & g, Node * start, Node * end, Path<GT> & path)
448 {
449 return find_path(g, start, end, path);
450 }
451
459 Path<GT> operator()(const GT & g, Node * start, Node * end)
460 {
461 Path<GT> path(g);
462 find_path(g, start, end, path);
463 return path;
464 }
465
466#undef BIBFS_INFO
467#undef BIBFS_DIR
468#undef BIBFS_FWD_PARENT
469#undef BIBFS_BCK_PARENT
470#undef BIBFS_FWD_PARC
471#undef BIBFS_BCK_PARC
472#undef BIBFS_FWD_DIST
473#undef BIBFS_BCK_DIST
474};
475
476} // end namespace Aleph
477
478# endif // BIDIRECTIONAL_BFS_H
#define BIBFS_DIR(p)
#define BIBFS_FWD_DIST(p)
#define BIBFS_BCK_PARENT(p)
#define BIBFS_BCK_DIST(p)
#define BIBFS_FWD_PARENT(p)
#define BIBFS_INFO(p)
#define BIBFS_BCK_PARC(p)
#define BIBFS_FWD_PARC(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:527
#define ah_runtime_error_if(C)
Throws std::runtime_error if condition holds.
Definition ah-errors.H:271
C++20 concepts for the protocol shared by graph algorithms.
List_Graph< Graph_Node< Node_Info >, Graph_Arc< Arc_Info > > GT
RAII guard for BiBFS initialization and cleanup.
BiBFS_Init_Guard(const BiBFS_Init_Guard &)=delete
Deleted copy constructor.
BiBFS_Init_Guard & operator=(const BiBFS_Init_Guard &)=delete
Deleted copy assignment.
BiBFS_Init_Guard(BiBFS_Init_Guard &&)=delete
Deleted move constructor.
BiBFS_Init_Guard(Bidirectional_BFS &__owner, const GT &__g) noexcept
Constructor.
Bidirectional BFS for finding shortest unweighted paths.
Path< GT > operator()(const GT &g, Node *start, Node *end)
Finds shortest path (operator interface returning path).
void build_path(const GT &g, Node *start, Node *meeting, Node *end, Path< GT > &path)
Node *& parent_ref(Node *p, const int dir) noexcept
bool is_seen(Node *p, const int dir) const noexcept
static constexpr int UNSEEN
static constexpr int BACKWARD
void mark_seen(Node *p, const int dir) noexcept
void expand_frontier(const GT &g, DynListQueue< Node * > &frontier, const int my_dir, const int other_dir, size_t &best_len, Node *&best_meeting)
static constexpr size_t INF_DIST
static constexpr int FORWARD
bool find_path(const GT &g, Node *start, Node *end, Path< GT > &path)
Finds the shortest unweighted path between start and end.
Arc *& parent_arc_ref(Node *p, const int dir) noexcept
void set_dist(Node *p, const int dir, const size_t d) noexcept
Bidirectional_BFS(SA __sa=SA())
Constructor.
Bidirectional_BFS(SA &__sa)
Constructor with lvalue arc filter.
size_t get_dist(Node *p, const int dir) const noexcept
bool operator()(const GT &g, Node *start, Node *end, Path< GT > &path)
Finds shortest path (operator interface with path output).
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.
Doubly-linked list (defined in tpl_dynList.H).
Definition htlist.H:1155
T & insert(const T &item)
Definition htlist.H:1220
Path on a graph.
Definition tpl_graph.H:2772
void empty()
Clean the path: all the nodes and arc are removed.
Definition tpl_graph.H:2922
void set_graph(const GT &__g, Node *start_node=nullptr)
Set the graph of the path.
Definition tpl_graph.H:2900
void append(Arc *arc)
Append an arc to the path.
Definition tpl_graph.H:2975
constexpr size_t get_num_nodes() const noexcept
Return the total of nodes of graph.
Definition graph-dry.H:737
Node * get_connected_node(Arc *arc, Node *node) const noexcept
Return the adjacent node to node through arc.
Definition graph-dry.H:820
#define NODE_COOKIE(p)
Return the node cookie
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
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
Itor lower_bound(Itor beg, Itor end, const T &value)
Find lower bound in a sorted range.
Definition ahAlgo.H:1190
and
Check uniqueness with explicit hash + equality functors.
static std::atomic< bool > init
Definition hash-fct.C:54
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
Dynamic queue implementation based on linked lists.
Utility algorithms and operations for graphs.