Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
Dial.H
Go to the documentation of this file.
1/*
2 Aleph_w
3
4 Data structures & Algorithms
5 version 2.0.0b
6 https://github.com/lrleon/Aleph-w
7
8 This file is part of Aleph-w library
9
10 Copyright (c) 2002-2026 Leandro Rabindranath Leon
11
12 Permission is hereby granted, free of charge, to any person obtaining a copy
13 of this software and associated documentation files (the "Software"), to deal
14 in the Software without restriction, including without limitation the rights
15 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16 copies of the Software, and to permit persons to whom the Software is
17 furnished to do so, subject to the following conditions:
18
19 The above copyright notice and this permission notice shall be included in all
20 copies or substantial portions of the Software.
21
22 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28 SOFTWARE.
29*/
30
31
82# ifndef DIAL_H
83# define DIAL_H
84
85# include <ah-graph-concepts.H>
86
87# include <limits>
88# include <type_traits>
89# include <tpl_array.H>
90# include <tpl_graph_utils.H>
91# include <ah-errors.H>
92
93namespace Aleph
94{
123 template <AlephGraph GT,
125 template <typename, class> class Itor = Node_Arc_Iterator,
128 {
129 public:
131 using Node = typename GT::Node;
132 using Arc = typename GT::Arc;
133
134 static_assert(std::is_integral_v<Distance_Type>,
135 "Dial's algorithm requires integral distance type");
136
137 private:
138 SA sa;
140 bool painted = false;
141 GT *ptr_g = nullptr;
142 Node *s = nullptr;
143 static constexpr size_t Max_Sane_Buckets = 16u * 1024u * 1024u;
144
145 static constexpr Distance_Type Inf =
146 std::numeric_limits<Distance_Type>::max();
147
156
157#define DIAL_INFO(p) (static_cast<Node_Info *>(NODE_COOKIE(p)))
158#define DIAL_DIST(p) (DIAL_INFO(p)->dist)
159#define DIAL_PARENT(p) (DIAL_INFO(p)->parent)
160#define DIAL_PARC(p) (DIAL_INFO(p)->parent_arc)
161
163 {
164 painted = false;
165 ptr_g = nullptr;
166 s = nullptr;
167 }
168
171 {
173 const GT * g = nullptr;
174 bool committed = false;
175
176 public:
182 : owner(&__owner), g(&__g)
183 {
184 // empty
185 }
186
195
197 void commit() noexcept { committed = true; }
198
201 {
202 if (not committed)
203 {
206 }
207 }
208 };
209
210 void init(const GT & g)
211 {
212 ptr_g = &const_cast<GT &>(g);
213 for (typename GT::Node_Iterator it(g); it.has_curr(); it.next())
214 {
215 auto p = it.get_curr();
217 NODE_COOKIE(p) = nullptr;
218 }
219 for (typename GT::Arc_Iterator it(g); it.has_curr(); it.next())
220 g.reset_bit(it.get_curr(), Aleph::Spanning_Tree);
221
222 try
223 {
224 for (typename GT::Node_Iterator it(g); it.has_curr(); it.next())
225 NODE_COOKIE(it.get_curr()) = new Node_Info;
226 }
227 catch (...)
228 {
231 throw;
232 }
233 }
234
236 {
237 for (typename GT::Node_Iterator it(*ptr_g); it.has_curr(); it.next())
238 {
239 auto p = it.get_curr();
240 auto info = DIAL_INFO(p);
241 auto parent = info->parent;
242 delete info;
243 NODE_COOKIE(p) = parent;
244 }
245 }
246
248 {
249 if (ptr_g == nullptr)
250 return;
251
252 for (typename GT::Node_Iterator it(*ptr_g); it.has_curr(); it.next())
253 {
254 auto p = it.get_curr();
255 auto info = DIAL_INFO(p);
256 if (info != nullptr)
257 delete info;
258 NODE_COOKIE(p) = nullptr;
260 }
261
262 for (typename GT::Arc_Iterator it(*ptr_g); it.has_curr(); it.next())
263 ptr_g->reset_bit(it.get_curr(), Aleph::Spanning_Tree);
264 }
265
267 {
269 for (typename GT::Arc_Iterator it(g); it.has_curr(); it.next())
270 {
271 auto w = distance(it.get_curr());
272 ah_domain_error_if(w < 0) << "Dial: negative weight " << w << " not allowed";
273 if (w > max_w)
274 max_w = w;
275 }
276 return max_w;
277 }
278
279 void run_dial(const GT & g, Node *start, Node *end = nullptr)
280 {
281 auto max_w = find_max_weight(g);
282
283 if (max_w == 0)
284 {
285 // All edges have weight 0: simple BFS
286 DIAL_DIST(start) = 0;
287 NODE_BITS(start).set_bit(Aleph::Spanning_Tree, true);
288
289 DynList<Node *> queue;
290 queue.append(start);
291
292 while (not queue.is_empty())
293 {
294 auto curr = queue.remove_first();
295
296 if (end != nullptr and curr == end)
297 break;
298
299 for (Itor<GT, SA> it(curr, sa); it.has_curr(); it.next())
300 {
301 auto arc = it.get_current_arc();
302 auto tgt = g.get_connected_node(arc, curr);
303
304 if (DIAL_DIST(tgt) > 0)
305 {
306 DIAL_DIST(tgt) = 0;
307 DIAL_PARENT(tgt) = curr;
308 DIAL_PARC(tgt) = arc;
309 NODE_BITS(tgt).set_bit(Aleph::Spanning_Tree, true);
310 ARC_BITS(arc).set_bit(Aleph::Spanning_Tree, true);
311 queue.append(tgt);
312 }
313 }
314 }
315 return;
316 }
317
318 auto num_nodes = g.get_num_nodes();
319 const size_t path_edge_bound = num_nodes > 0 ? num_nodes - 1 : 0;
320 const size_t max_w_size = static_cast<size_t>(max_w);
321
322 // Maximum possible shortest-path distance in this graph:
323 // (|V|-1) * max_w. Compute it with checked size_t arithmetic.
325 path_edge_bound > std::numeric_limits<size_t>::max() / max_w_size)
326 << "Dial: overflow computing max_dist = (|V|-1) * max_w with |V|="
327 << num_nodes << " and max_w=" << max_w;
328
330 ah_overflow_error_if(max_dist_size > static_cast<size_t>(std::numeric_limits<Distance_Type>::max()))
331 << "Dial: max_dist " << max_dist_size
332 << " exceeds Distance_Type range; use a wider type or Dijkstra";
333 ah_overflow_error_if(max_dist_size == std::numeric_limits<size_t>::max())
334 << "Dial: overflow computing number of buckets";
335
337 const size_t num_buckets = max_dist_size + 1;
339 << "Dial: bucket count " << num_buckets
340 << " exceeds safety limit " << Max_Sane_Buckets
341 << "; use Dijkstra_Min_Paths for large weight ranges";
342
343 Array<DynList<Node *>> buckets(num_buckets, DynList<Node *>());
344
345 DIAL_DIST(start) = 0;
346 NODE_BITS(start).set_bit(Aleph::Spanning_Tree, true);
347 buckets[0].append(start);
348
349 size_t processed = 0;
350
351 for (size_t idx = 0; idx < num_buckets and processed < num_nodes; ++idx)
352 {
353 while (not buckets[idx].is_empty())
354 {
355 auto curr = buckets[idx].remove_first();
356 auto curr_dist = DIAL_DIST(curr);
357
358 // Skip if this entry is stale (node was already relaxed to
359 // a shorter distance)
360 if (static_cast<size_t>(curr_dist) != idx)
361 continue;
362
363 ++processed;
364
365 if (end != nullptr and curr == end)
366 return;
367
368 for (Itor<GT, SA> it(curr, sa); it.has_curr(); it.next())
369 {
370 auto arc = it.get_current_arc();
371 auto tgt = g.get_connected_node(arc, curr);
372 auto w = distance(arc);
373 ah_domain_error_if(w < 0) << "Dial: negative weight " << w
374 << " not allowed";
375 ah_overflow_error_if(curr_dist > std::numeric_limits<Distance_Type>::max() - w)
376 << "Dial: overflow computing new_dist = curr_dist + w";
377 auto new_dist = curr_dist + w;
378
379 // Distances above the computed bound are not representable in buckets.
380 if (new_dist > max_dist)
381 continue;
382
383 if (new_dist < DIAL_DIST(tgt))
384 {
385 // Clear previous Spanning_Tree mark for tgt's parent arc
386 // to correctly handle multi-edges and avoid stale marks.
387 if (DIAL_PARC(tgt) != nullptr)
388 ARC_BITS(DIAL_PARC(tgt)).set_bit(Aleph::Spanning_Tree, false);
389
390 DIAL_DIST(tgt) = new_dist;
391 DIAL_PARENT(tgt) = curr;
392 DIAL_PARC(tgt) = arc;
393 NODE_BITS(tgt).set_bit(Aleph::Spanning_Tree, true);
394 ARC_BITS(arc).set_bit(Aleph::Spanning_Tree, true);
395 buckets[static_cast<size_t>(new_dist)].append(tgt);
396 }
397 }
398 }
399 }
400 }
401
402 public:
409 : sa(__sa), distance(dist)
410 {
411 // empty
412 }
413
418
423
428
446 void paint_min_paths_tree(const GT & g, Node *start)
447 {
448 ah_domain_error_if(start == nullptr) << "start node cannot be null";
449 ah_domain_error_if(g.get_num_nodes() == 0) << "graph is empty";
450
451 painted = false;
452 s = nullptr;
453
454 init(g);
455 Dial_Init_Guard guard(*this, g);
456 s = start;
457 run_dial(g, start);
458 uninit_paint();
459 guard.commit();
460 painted = true;
461 }
462
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
485 for (auto curr = node; curr != s;)
486 {
487 auto parent = static_cast<Node *>(NODE_COOKIE(curr));
488 ah_domain_error_if(parent == nullptr)
489 << "Dial: path reconstruction failed (null parent)";
490
491 bool found = false;
492 for (Itor<GT, SA> it(parent, sa); it.has_curr(); it.next())
493 {
494 auto arc = it.get_current_arc();
495 auto tgt = ptr_g->get_connected_node(arc, parent);
496 if (tgt == curr and IS_ARC_VISITED(arc, Aleph::Spanning_Tree))
497 {
498 total += distance(arc);
499 found = true;
500 break;
501 }
502 }
504 << "Dial: path reconstruction failed (arc not found)";
505
506 curr = parent;
507 }
508
509 return total;
510 }
511
523 {
524 ah_domain_error_if(not painted) << "Graph has not been painted";
525 ah_domain_error_if(ptr_g == nullptr) << "No computation has been done";
526
527 return Aleph::get_min_path<GT, Distance>(s, end, path);
528 }
529
549 Distance_Type find_min_path(const GT & g, Node *start, Node *end,
550 Path<GT> & path)
551 {
552 ah_domain_error_if(start == nullptr) << "start node cannot be null";
553 ah_domain_error_if(end == nullptr) << "end node cannot be null";
554 ah_domain_error_if(g.get_num_nodes() == 0) << "graph is empty";
555
556 path.empty();
557
558 painted = false;
559 s = nullptr;
560
561 init(g);
562 Dial_Init_Guard guard(*this, g);
563 s = start;
564 run_dial(g, start, end);
565 uninit_paint();
566 guard.commit();
567 painted = true;
568
570 return Inf;
571
572 return get_min_path(end, path);
573 }
574
589 Distance_Type operator()(const GT & g, Node *start, Node *end,
590 Path<GT> & path)
591 {
592 return find_min_path(g, start, end, path);
593 }
594
595#undef DIAL_INFO
596#undef DIAL_DIST
597#undef DIAL_PARENT
598#undef DIAL_PARC
599 };
600} // end namespace Aleph
601
602# endif // DIAL_H
#define DIAL_PARC(p)
Definition Dial.H:160
#define DIAL_PARENT(p)
Definition Dial.H:159
#define DIAL_DIST(p)
Definition Dial.H:158
#define DIAL_INFO(p)
Definition Dial.H:157
Exception handling system with formatted messages for Aleph-w.
#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
#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
long double w
Definition btreepic.C:153
int num_nodes
Definition btreepic.C:410
Simple dynamic array with automatic resizing and functional operations.
Definition tpl_array.H:138
T & append(const T &data)
Append a copy of data
Definition tpl_array.H:250
Default distance accessor for arc weights.
RAII guard for Dial initialization and cleanup.
Definition Dial.H:171
Dial_Init_Guard & operator=(const Dial_Init_Guard &)=delete
Deleted copy assignment.
~Dial_Init_Guard() noexcept
Destructor.
Definition Dial.H:200
void commit() noexcept
Commits the operation, preventing cleanup on destruction.
Definition Dial.H:197
Dial_Init_Guard(Dial_Init_Guard &&)=delete
Deleted move constructor.
Dial_Init_Guard(Dial_Min_Paths &__owner, const GT &__g) noexcept
Constructor.
Definition Dial.H:181
Dial_Init_Guard(const Dial_Init_Guard &)=delete
Deleted copy constructor.
Dial's algorithm for shortest paths with integer weights.
Definition Dial.H:128
Distance_Type get_distance(Node *node)
Gets the accumulated distance to a node after painting.
Definition Dial.H:473
Distance_Type get_min_path(Node *end, Path< GT > &path)
Extracts a shortest path from a previously painted graph.
Definition Dial.H:522
GT * get_graph() const noexcept
Get the graph of the last computation.
Definition Dial.H:427
void run_dial(const GT &g, Node *start, Node *end=nullptr)
Definition Dial.H:279
void paint_min_paths_tree(const GT &g, Node *start)
Paints the shortest paths tree on the graph from start.
Definition Dial.H:446
void init(const GT &g)
Definition Dial.H:210
Node * get_start_node() const noexcept
Get the start node of the last computation.
Definition Dial.H:422
bool is_painted() const noexcept
Check if the graph has been painted.
Definition Dial.H:417
static constexpr Distance_Type Inf
Definition Dial.H:145
typename GT::Node Node
Definition Dial.H:131
Distance distance
Definition Dial.H:139
static constexpr size_t Max_Sane_Buckets
Definition Dial.H:143
Distance_Type operator()(const GT &g, Node *start, Node *end, Path< GT > &path)
Computes shortest path (operator interface).
Definition Dial.H:589
Distance_Type find_min_path(const GT &g, Node *start, Node *end, Path< GT > &path)
Computes the shortest path from start to end.
Definition Dial.H:549
typename Distance::Distance_Type Distance_Type
Definition Dial.H:130
Distance_Type find_max_weight(const GT &g)
Definition Dial.H:266
typename GT::Arc Arc
Definition Dial.H:132
Dial_Min_Paths(Distance dist=Distance(), SA __sa=SA())
Constructor.
Definition Dial.H:408
void uninit_paint()
Definition Dial.H:235
void uninit_discard()
Definition Dial.H:247
void reset_state_after_failure() noexcept
Definition Dial.H:162
Doubly-linked list (defined in tpl_dynList.H).
Definition htlist.H:1155
T & append(const T &item)
Definition htlist.H:1271
constexpr bool is_empty() const noexcept
Definition htlist.H:419
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
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
void reset_bit(Node *node, int bit) const noexcept
Reset the bit of node (to zero)
Definition graph-dry.H:849
#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
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.
@ 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.
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
Distance accessor.
Dynamic array container with automatic resizing.
Utility algorithms and operations for graphs.