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 <limits>
78# include <tpl_dynListQueue.H>
79# include <tpl_graph_utils.H>
80# include <ah-errors.H>
81
82namespace Aleph
83{
84
112template <class GT,
113 template <typename, class> class Fwd_Itor = Node_Arc_Iterator,
114 template <typename, class> class Bck_Itor = Node_Arc_Iterator,
115 class SA = Dft_Show_Arc<GT>>
117{
118public:
119 using Node = typename GT::Node;
120 using Arc = typename GT::Arc;
121
122private:
124
125 // Direction constants
126 static constexpr int UNSEEN = 0;
127 static constexpr int FORWARD = 1;
128 static constexpr int BACKWARD = 2;
129 static constexpr size_t INF_DIST = std::numeric_limits<size_t>::max();
130
131 struct BFS_Info
132 {
133 Node * fwd_parent = nullptr;
134 Node * bck_parent = nullptr;
135 Arc * fwd_parent_arc = nullptr;
136 Arc * bck_parent_arc = nullptr;
139 int dir = UNSEEN;
140 };
141
142#define BIBFS_INFO(p) (static_cast<BFS_Info *>(NODE_COOKIE(p)))
143#define BIBFS_DIR(p) (BIBFS_INFO(p)->dir)
144#define BIBFS_FWD_PARENT(p) (BIBFS_INFO(p)->fwd_parent)
145#define BIBFS_BCK_PARENT(p) (BIBFS_INFO(p)->bck_parent)
146#define BIBFS_FWD_PARC(p) (BIBFS_INFO(p)->fwd_parent_arc)
147#define BIBFS_BCK_PARC(p) (BIBFS_INFO(p)->bck_parent_arc)
148#define BIBFS_FWD_DIST(p) (BIBFS_INFO(p)->fwd_dist)
149#define BIBFS_BCK_DIST(p) (BIBFS_INFO(p)->bck_dist)
150
153 {
155 const GT * g = nullptr;
156
157 public:
163 : owner(&__owner), g(&__g)
164 {
165 // empty
166 }
167
176
179 {
180 owner->cleanup(*g);
181 }
182 };
183
184 void init(const GT & g)
185 {
186 for (typename GT::Node_Iterator it(g); it.has_curr(); it.next())
187 NODE_COOKIE(it.get_curr()) = nullptr;
188
189 try
190 {
191 for (typename GT::Node_Iterator it(g); it.has_curr(); it.next())
192 {
193 auto p = it.get_curr();
194 NODE_COOKIE(p) = new BFS_Info;
195 }
196 }
197 catch (...)
198 {
199 cleanup(g);
200 throw;
201 }
202 }
203
204 void cleanup(const GT & g)
205 {
206 for (typename GT::Node_Iterator it(g); it.has_curr(); it.next())
207 {
208 auto p = it.get_curr();
209 delete BIBFS_INFO(p);
210 NODE_COOKIE(p) = nullptr;
211 }
212 }
213
214 [[nodiscard]] bool is_seen(Node * p, const int dir) const noexcept
215 {
216 return (BIBFS_DIR(p) & dir) != 0;
217 }
218
219 void mark_seen(Node * p, const int dir) noexcept
220 {
221 BIBFS_DIR(p) |= dir;
222 }
223
224 [[nodiscard]] size_t get_dist(Node * p, const int dir) const noexcept
225 {
226 return dir == FORWARD ? BIBFS_FWD_DIST(p) : BIBFS_BCK_DIST(p);
227 }
228
229 void set_dist(Node * p, const int dir, const size_t d) noexcept
230 {
231 if (dir == FORWARD)
232 BIBFS_FWD_DIST(p) = d;
233 else
234 BIBFS_BCK_DIST(p) = d;
235 }
236
237 Node *& parent_ref(Node * p, const int dir) noexcept
238 {
239 if (dir == FORWARD)
240 return BIBFS_FWD_PARENT(p);
241 return BIBFS_BCK_PARENT(p);
242 }
243
244 Arc *& parent_arc_ref(Node * p, const int dir) noexcept
245 {
246 if (dir == FORWARD)
247 return BIBFS_FWD_PARC(p);
248 return BIBFS_BCK_PARC(p);
249 }
250
251 template <template <typename, class> class Itor>
253 const int my_dir, const int other_dir,
254 size_t & best_len, Node *& best_meeting)
255 {
257
258 while (not frontier.is_empty())
259 {
260 auto curr = frontier.get();
261 const auto curr_dist = get_dist(curr, my_dir);
262
263 for (Itor<GT, SA> it(curr, sa); it.has_curr(); it.next())
264 {
265 auto arc = it.get_current_arc();
266 auto tgt = g.get_connected_node(arc, curr);
267
268 const bool seen_my = is_seen(tgt, my_dir);
269 if (not seen_my)
270 {
271 mark_seen(tgt, my_dir);
272 parent_ref(tgt, my_dir) = curr;
273 parent_arc_ref(tgt, my_dir) = arc;
274 set_dist(tgt, my_dir, curr_dist + 1);
275 next_frontier.put(tgt);
276 }
277
278 if (is_seen(tgt, other_dir))
279 {
280 const auto my_dist = get_dist(tgt, my_dir);
281 const auto other_dist = get_dist(tgt, other_dir);
282
284 {
285 const auto candidate_len = my_dist + other_dist;
287 {
289 best_meeting = tgt;
290 }
291 }
292 }
293 }
294 }
295
297 }
298
299 void build_path(const GT & g, Node * start, Node * meeting, Node * end,
300 Path<GT> & path)
301 {
302 path.empty();
303
304 if (start == end)
305 {
306 path.set_graph(g, start);
307 return;
308 }
309
310 path.set_graph(g, start);
311
312 // Reconstruct start -> meeting using stored forward parent arcs.
314 for (auto curr = meeting; curr != start; curr = BIBFS_FWD_PARENT(curr))
315 {
316 ah_runtime_error_if(BIBFS_FWD_PARENT(curr) == nullptr)
317 << "Bidirectional_BFS: inconsistent forward parent chain";
318 ah_runtime_error_if(BIBFS_FWD_PARC(curr) == nullptr)
319 << "Bidirectional_BFS: missing forward parent arc";
320 fwd_arcs.insert(BIBFS_FWD_PARC(curr)); // prepend for start->meeting order
321 }
322
323 for (auto it = fwd_arcs.get_it(); it.has_curr(); it.next())
324 path.append(it.get_curr());
325
326 // Reconstruct meeting -> end using stored backward parent arcs.
327 for (auto curr = meeting; curr != end; curr = BIBFS_BCK_PARENT(curr))
328 {
329 ah_runtime_error_if(BIBFS_BCK_PARENT(curr) == nullptr)
330 << "Bidirectional_BFS: inconsistent backward parent chain";
331 ah_runtime_error_if(BIBFS_BCK_PARC(curr) == nullptr)
332 << "Bidirectional_BFS: missing backward parent arc";
333 path.append(BIBFS_BCK_PARC(curr));
334 }
335 }
336
337public:
343
349
369 bool find_path(const GT & g, Node * start, Node * end, Path<GT> & path)
370 {
371 ah_domain_error_if(start == nullptr) << "start node cannot be null";
372 ah_domain_error_if(end == nullptr) << "end node cannot be null";
373 ah_domain_error_if(g.get_num_nodes() == 0) << "graph is empty";
374
375 path.empty();
376
377 if (start == end)
378 {
379 path.set_graph(g, start);
380 return true;
381 }
382
383 init(g);
384 BiBFS_Init_Guard guard(*this, g);
385
386 bool found = false;
387 Node * meeting = nullptr;
388
389 mark_seen(start, FORWARD);
390 mark_seen(end, BACKWARD);
391 set_dist(start, FORWARD, 0);
392 set_dist(end, BACKWARD, 0);
393
396
397 fwd_frontier.put(start);
398 bck_frontier.put(end);
399
400 size_t fwd_depth = 0;
401 size_t bck_depth = 0;
402 size_t best_len = INF_DIST;
403
404 while (not fwd_frontier.is_empty() and not bck_frontier.is_empty())
405 {
406 const bool expand_forward = fwd_frontier.size() <= bck_frontier.size();
407
408 if (expand_forward)
409 {
412 ++fwd_depth;
413 }
414 else
415 {
418 ++bck_depth;
419 }
420
421 if (meeting != nullptr)
422 {
423 const size_t min_depth = fwd_depth < bck_depth ? fwd_depth : bck_depth;
424 const size_t lower_bound = min_depth + 1;
425 if (lower_bound >= best_len)
426 break;
427 }
428 }
429
430 found = meeting != nullptr;
431 if (found)
432 build_path(g, start, meeting, end, path);
433
434 return found;
435 }
436
445 bool operator()(const GT & g, Node * start, Node * end, Path<GT> & path)
446 {
447 return find_path(g, start, end, path);
448 }
449
457 Path<GT> operator()(const GT & g, Node * start, Node * end)
458 {
459 Path<GT> path(g);
460 find_path(g, start, end, path);
461 return path;
462 }
463
464#undef BIBFS_INFO
465#undef BIBFS_DIR
466#undef BIBFS_FWD_PARENT
467#undef BIBFS_BCK_PARENT
468#undef BIBFS_FWD_PARC
469#undef BIBFS_BCK_PARC
470#undef BIBFS_FWD_DIST
471#undef BIBFS_BCK_DIST
472};
473
474} // end namespace Aleph
475
476# 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:522
#define ah_runtime_error_if(C)
Throws std::runtime_error if condition holds.
Definition ah-errors.H:266
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.
Dynamic singly linked list with functional programming support.
Definition htlist.H:1155
T & insert(const T &item)
Definition htlist.H:1220
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
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
#define NODE_COOKIE(p)
Return the node cookie
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
and
Check uniqueness with explicit hash + equality functors.
Itor lower_bound(Itor beg, Itor end, const T &value)
Find lower bound in a sorted range.
Definition ahAlgo.H:1190
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
Dynamic queue implementation based on linked lists.
Utility algorithms and operations for graphs.