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 <limits>
86# include <type_traits>
87# include <tpl_array.H>
88# include <tpl_graph_utils.H>
89# include <ah-errors.H>
90
91namespace Aleph
92{
121 template <class GT,
122 class Distance = Dft_Dist<GT>,
123 template <typename, class> class Itor = Node_Arc_Iterator,
124 class SA = Dft_Show_Arc<GT>>
126 {
127 public:
129 using Node = typename GT::Node;
130 using Arc = typename GT::Arc;
131
132 static_assert(std::is_integral_v<Distance_Type>,
133 "Dial's algorithm requires integral distance type");
134
135 private:
138 bool painted = false;
139 GT *ptr_g = nullptr;
140 Node *s = nullptr;
141 static constexpr size_t Max_Sane_Buckets = 16u * 1024u * 1024u;
142
143 static constexpr Distance_Type Inf =
144 std::numeric_limits<Distance_Type>::max();
145
154
155#define DIAL_INFO(p) (static_cast<Node_Info *>(NODE_COOKIE(p)))
156#define DIAL_DIST(p) (DIAL_INFO(p)->dist)
157#define DIAL_PARENT(p) (DIAL_INFO(p)->parent)
158#define DIAL_PARC(p) (DIAL_INFO(p)->parent_arc)
159
161 {
162 painted = false;
163 ptr_g = nullptr;
164 s = nullptr;
165 }
166
169 {
171 const GT * g = nullptr;
172 bool committed = false;
173
174 public:
180 : owner(&__owner), g(&__g)
181 {
182 // empty
183 }
184
193
195 void commit() noexcept { committed = true; }
196
199 {
200 if (not committed)
201 {
204 }
205 }
206 };
207
208 void init(const GT & g)
209 {
210 ptr_g = &const_cast<GT &>(g);
211 for (typename GT::Node_Iterator it(g); it.has_curr(); it.next())
212 {
213 auto p = it.get_curr();
215 NODE_COOKIE(p) = nullptr;
216 }
217 for (typename GT::Arc_Iterator it(g); it.has_curr(); it.next())
218 g.reset_bit(it.get_curr(), Aleph::Spanning_Tree);
219
220 try
221 {
222 for (typename GT::Node_Iterator it(g); it.has_curr(); it.next())
223 NODE_COOKIE(it.get_curr()) = new Node_Info;
224 }
225 catch (...)
226 {
229 throw;
230 }
231 }
232
234 {
235 for (typename GT::Node_Iterator it(*ptr_g); it.has_curr(); it.next())
236 {
237 auto p = it.get_curr();
238 auto info = DIAL_INFO(p);
239 auto parent = info->parent;
240 delete info;
241 NODE_COOKIE(p) = parent;
242 }
243 }
244
246 {
247 if (ptr_g == nullptr)
248 return;
249
250 for (typename GT::Node_Iterator it(*ptr_g); it.has_curr(); it.next())
251 {
252 auto p = it.get_curr();
253 auto info = DIAL_INFO(p);
254 if (info != nullptr)
255 delete info;
256 NODE_COOKIE(p) = nullptr;
258 }
259
260 for (typename GT::Arc_Iterator it(*ptr_g); it.has_curr(); it.next())
261 ptr_g->reset_bit(it.get_curr(), Aleph::Spanning_Tree);
262 }
263
265 {
267 for (typename GT::Arc_Iterator it(g); it.has_curr(); it.next())
268 {
269 auto w = distance(it.get_curr());
270 ah_domain_error_if(w < 0) << "Dial: negative weight " << w << " not allowed";
271 if (w > max_w)
272 max_w = w;
273 }
274 return max_w;
275 }
276
277 void run_dial(const GT & g, Node *start, Node *end = nullptr)
278 {
279 auto max_w = find_max_weight(g);
280
281 if (max_w == 0)
282 {
283 // All edges have weight 0: simple BFS
284 DIAL_DIST(start) = 0;
285 NODE_BITS(start).set_bit(Aleph::Spanning_Tree, true);
286
287 DynList<Node *> queue;
288 queue.append(start);
289
290 while (not queue.is_empty())
291 {
292 auto curr = queue.remove_first();
293
294 if (end != nullptr and curr == end)
295 break;
296
297 for (Itor<GT, SA> it(curr, sa); it.has_curr(); it.next())
298 {
299 auto arc = it.get_current_arc();
300 auto tgt = g.get_connected_node(arc, curr);
301
302 if (DIAL_DIST(tgt) > 0)
303 {
304 DIAL_DIST(tgt) = 0;
305 DIAL_PARENT(tgt) = curr;
306 DIAL_PARC(tgt) = arc;
307 NODE_BITS(tgt).set_bit(Aleph::Spanning_Tree, true);
308 ARC_BITS(arc).set_bit(Aleph::Spanning_Tree, true);
309 queue.append(tgt);
310 }
311 }
312 }
313 return;
314 }
315
316 auto num_nodes = g.get_num_nodes();
317 const size_t path_edge_bound = num_nodes > 0 ? num_nodes - 1 : 0;
318 const size_t max_w_size = static_cast<size_t>(max_w);
319
320 // Maximum possible shortest-path distance in this graph:
321 // (|V|-1) * max_w. Compute it with checked size_t arithmetic.
323 path_edge_bound > std::numeric_limits<size_t>::max() / max_w_size)
324 << "Dial: overflow computing max_dist = (|V|-1) * max_w with |V|="
325 << num_nodes << " and max_w=" << max_w;
326
328 ah_overflow_error_if(max_dist_size > static_cast<size_t>(std::numeric_limits<Distance_Type>::max()))
329 << "Dial: max_dist " << max_dist_size
330 << " exceeds Distance_Type range; use a wider type or Dijkstra";
331 ah_overflow_error_if(max_dist_size == std::numeric_limits<size_t>::max())
332 << "Dial: overflow computing number of buckets";
333
335 const size_t num_buckets = max_dist_size + 1;
337 << "Dial: bucket count " << num_buckets
338 << " exceeds safety limit " << Max_Sane_Buckets
339 << "; use Dijkstra_Min_Paths for large weight ranges";
340
341 Array<DynList<Node *>> buckets(num_buckets, DynList<Node *>());
342
343 DIAL_DIST(start) = 0;
344 NODE_BITS(start).set_bit(Aleph::Spanning_Tree, true);
345 buckets[0].append(start);
346
347 size_t processed = 0;
348
349 for (size_t idx = 0; idx < num_buckets and processed < num_nodes; ++idx)
350 {
351 while (not buckets[idx].is_empty())
352 {
353 auto curr = buckets[idx].remove_first();
354 auto curr_dist = DIAL_DIST(curr);
355
356 // Skip if this entry is stale (node was already relaxed to
357 // a shorter distance)
358 if (static_cast<size_t>(curr_dist) != idx)
359 continue;
360
361 ++processed;
362
363 if (end != nullptr and curr == end)
364 return;
365
366 for (Itor<GT, SA> it(curr, sa); it.has_curr(); it.next())
367 {
368 auto arc = it.get_current_arc();
369 auto tgt = g.get_connected_node(arc, curr);
370 auto w = distance(arc);
371 ah_domain_error_if(w < 0) << "Dial: negative weight " << w
372 << " not allowed";
373 ah_overflow_error_if(curr_dist > std::numeric_limits<Distance_Type>::max() - w)
374 << "Dial: overflow computing new_dist = curr_dist + w";
375 auto new_dist = curr_dist + w;
376
377 // Distances above the computed bound are not representable in buckets.
378 if (new_dist > max_dist)
379 continue;
380
381 if (new_dist < DIAL_DIST(tgt))
382 {
383 // Clear previous Spanning_Tree mark for tgt's parent arc
384 // to correctly handle multi-edges and avoid stale marks.
385 if (DIAL_PARC(tgt) != nullptr)
386 ARC_BITS(DIAL_PARC(tgt)).set_bit(Aleph::Spanning_Tree, false);
387
388 DIAL_DIST(tgt) = new_dist;
389 DIAL_PARENT(tgt) = curr;
390 DIAL_PARC(tgt) = arc;
391 NODE_BITS(tgt).set_bit(Aleph::Spanning_Tree, true);
392 ARC_BITS(arc).set_bit(Aleph::Spanning_Tree, true);
393 buckets[static_cast<size_t>(new_dist)].append(tgt);
394 }
395 }
396 }
397 }
398 }
399
400 public:
407 : sa(__sa), distance(dist)
408 {
409 // empty
410 }
411
416
421
426
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 painted = false;
450 s = nullptr;
451
452 init(g);
453 Dial_Init_Guard guard(*this, g);
454 s = start;
455 run_dial(g, start);
456 uninit_paint();
457 guard.commit();
458 painted = true;
459 }
460
472 {
473 ah_domain_error_if(not painted) << "Graph has not been painted";
474 ah_domain_error_if(node == nullptr) << "node cannot be null";
475
476 if (node == s)
477 return Distance_Type(0);
478
480 << "node is not reachable from start";
481
483 for (auto curr = node; curr != s;)
484 {
485 auto parent = static_cast<Node *>(NODE_COOKIE(curr));
486 ah_domain_error_if(parent == nullptr)
487 << "Dial: path reconstruction failed (null parent)";
488
489 bool found = false;
490 for (Itor<GT, SA> it(parent, sa); it.has_curr(); it.next())
491 {
492 auto arc = it.get_current_arc();
493 auto tgt = ptr_g->get_connected_node(arc, parent);
494 if (tgt == curr and IS_ARC_VISITED(arc, Aleph::Spanning_Tree))
495 {
496 total += distance(arc);
497 found = true;
498 break;
499 }
500 }
502 << "Dial: path reconstruction failed (arc not found)";
503
504 curr = parent;
505 }
506
507 return total;
508 }
509
521 {
522 ah_domain_error_if(not painted) << "Graph has not been painted";
523 ah_domain_error_if(ptr_g == nullptr) << "No computation has been done";
524
525 return Aleph::get_min_path<GT, Distance>(s, end, path);
526 }
527
547 Distance_Type find_min_path(const GT & g, Node *start, Node *end,
548 Path<GT> & path)
549 {
550 ah_domain_error_if(start == nullptr) << "start node cannot be null";
551 ah_domain_error_if(end == nullptr) << "end node cannot be null";
552 ah_domain_error_if(g.get_num_nodes() == 0) << "graph is empty";
553
554 path.empty();
555
556 painted = false;
557 s = nullptr;
558
559 init(g);
560 Dial_Init_Guard guard(*this, g);
561 s = start;
562 run_dial(g, start, end);
563 uninit_paint();
564 guard.commit();
565 painted = true;
566
568 return Inf;
569
570 return get_min_path(end, path);
571 }
572
587 Distance_Type operator()(const GT & g, Node *start, Node *end,
588 Path<GT> & path)
589 {
590 return find_min_path(g, start, end, path);
591 }
592
593#undef DIAL_INFO
594#undef DIAL_DIST
595#undef DIAL_PARENT
596#undef DIAL_PARC
597 };
598} // end namespace Aleph
599
600# endif // DIAL_H
#define DIAL_PARC(p)
Definition Dial.H:158
#define DIAL_PARENT(p)
Definition Dial.H:157
#define DIAL_DIST(p)
Definition Dial.H:156
#define DIAL_INFO(p)
Definition Dial.H:155
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:463
#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
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:139
T & append(const T &data)
Append a copy of data
Definition tpl_array.H:245
Default distance accessor for arc weights.
RAII guard for Dial initialization and cleanup.
Definition Dial.H:169
Dial_Init_Guard & operator=(const Dial_Init_Guard &)=delete
Deleted copy assignment.
~Dial_Init_Guard() noexcept
Destructor.
Definition Dial.H:198
void commit() noexcept
Commits the operation, preventing cleanup on destruction.
Definition Dial.H:195
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:179
Dial_Init_Guard(const Dial_Init_Guard &)=delete
Deleted copy constructor.
Dial's algorithm for shortest paths with integer weights.
Definition Dial.H:126
Distance_Type get_distance(Node *node)
Gets the accumulated distance to a node after painting.
Definition Dial.H:471
Distance_Type get_min_path(Node *end, Path< GT > &path)
Extracts a shortest path from a previously painted graph.
Definition Dial.H:520
GT * get_graph() const noexcept
Get the graph of the last computation.
Definition Dial.H:425
void run_dial(const GT &g, Node *start, Node *end=nullptr)
Definition Dial.H:277
void paint_min_paths_tree(const GT &g, Node *start)
Paints the shortest paths tree on the graph from start.
Definition Dial.H:444
void init(const GT &g)
Definition Dial.H:208
Node * get_start_node() const noexcept
Get the start node of the last computation.
Definition Dial.H:420
bool is_painted() const noexcept
Check if the graph has been painted.
Definition Dial.H:415
static constexpr Distance_Type Inf
Definition Dial.H:143
typename GT::Node Node
Definition Dial.H:129
Distance distance
Definition Dial.H:137
static constexpr size_t Max_Sane_Buckets
Definition Dial.H:141
Distance_Type operator()(const GT &g, Node *start, Node *end, Path< GT > &path)
Computes shortest path (operator interface).
Definition Dial.H:587
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:547
typename Distance::Distance_Type Distance_Type
Definition Dial.H:128
Distance_Type find_max_weight(const GT &g)
Definition Dial.H:264
typename GT::Arc Arc
Definition Dial.H:130
Dial_Min_Paths(Distance dist=Distance(), SA __sa=SA())
Constructor.
Definition Dial.H:406
void uninit_paint()
Definition Dial.H:233
void uninit_discard()
Definition Dial.H:245
void reset_state_after_failure() noexcept
Definition Dial.H:160
Dynamic singly linked list with functional programming support.
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:2669
void empty()
Clean the path: all the nodes and arc are removed.
Definition tpl_graph.H:2819
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
#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
Distance accessor.
Dynamic array container with automatic resizing.
Utility algorithms and operations for graphs.