Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
Min_Mean_Cycle.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
63# ifndef MIN_MEAN_CYCLE_H
64# define MIN_MEAN_CYCLE_H
65
66# include <cmath>
67# include <cstddef>
68# include <limits>
69# include <type_traits>
70# include <utility>
71
72# include <ah-errors.H>
73# include <shortest_path_common.H>
74# include <htlist.H>
75# include <tpl_array.H>
76# include <tpl_dynMapTree.H>
77
78namespace Aleph
79{
86 template <class GT, typename Cost_Type>
88 {
89 bool has_cycle = false;
90 long double minimum_mean = std::numeric_limits<long double>::infinity();
91 typename GT::Node * witness_node = nullptr;
92
94 size_t cycle_length = 0;
95
98 };
99
108 {
109 bool has_cycle = false;
110 long double minimum_mean = std::numeric_limits<long double>::infinity();
111 };
112
113
114 namespace min_mean_cycle_detail
115 {
116 template <class GT, typename Cost_Type>
118 {
119 size_t src_idx = 0;
120 typename GT::Arc * arc = nullptr;
122 };
123
124
125 template <class GT, class Distance, class SA>
126 void validate_weights(const GT & g, Distance distance, SA sa)
127 {
128 using Arc = typename GT::Arc;
129 using Cost_Type = typename Distance::Distance_Type;
130
131 static_assert(std::is_arithmetic_v<Cost_Type>,
132 "Karp minimum mean cycle requires arithmetic arc costs");
133
134 for (Arc_Iterator<GT, SA> it(g, sa); it.has_curr(); it.next_ne())
135 {
136 Arc * arc = it.get_curr_ne();
137 const Cost_Type w = distance(arc);
138
139 if constexpr (std::is_floating_point_v<Cost_Type>)
140 ah_domain_error_if(not std::isfinite(w))
141 << "Karp minimum mean cycle requires finite arc weights";
142 }
143 }
144
145
146 template <typename Cost_Type>
147 void validate_finite_accumulator(const Cost_Type & value, const char * context)
148 {
149 if constexpr (std::is_floating_point_v<Cost_Type>)
150 ah_domain_error_if(not std::isfinite(value))
151 << context;
152 }
153 } // namespace min_mean_cycle_detail
154
155
179 template <class GT,
180 class Distance = Dft_Dist<GT>,
181 class SA = Dft_Show_Arc<GT>>
184 Distance distance = Distance(),
185 SA sa = SA())
186 {
187 using Node = typename GT::Node;
188 using Arc = typename GT::Arc;
189 using Cost_Type = typename Distance::Distance_Type;
192
194 << "karp_minimum_mean_cycle(): graph must be directed";
195
197
198 Result result;
199 const size_t n = g.get_num_nodes();
200 if (n == 0)
201 return result;
202
204 n > std::numeric_limits<size_t>::max() - 1
205 or (n + 1) > std::numeric_limits<size_t>::max() / n)
206 << "karp_minimum_mean_cycle(): DP table size overflow";
207
209 nodes.reserve(n);
210
211 DynMapTree<Node *, size_t> node_to_idx;
212
213 size_t idx = 0;
214 for (Node_Iterator<GT> it(g); it.has_curr(); it.next_ne(), ++idx)
215 {
216 Node * node = it.get_curr_ne();
217 nodes.append(node);
218 node_to_idx.insert(node, idx);
219 }
220
222 incoming.reserve(n);
223 for (size_t i = 0; i < n; ++i)
224 incoming.append(Array<Incoming>());
225
226 for (Arc_Iterator<GT, SA> it(g, sa); it.has_curr(); it.next_ne())
227 {
228 Arc * arc = it.get_curr_ne();
229 Node * src = g.get_src_node(arc);
230 Node * tgt = g.get_tgt_node(arc);
231
232 const size_t src_idx = node_to_idx.find(src);
233 const size_t tgt_idx = node_to_idx.find(tgt);
234
235 incoming[tgt_idx].append(Incoming{src_idx, arc, distance(arc)});
236 }
237
238 const Cost_Type inf = std::numeric_limits<Cost_Type>::max();
239 const size_t total_states = (n + 1) * n;
240
244
245 const auto state_of = [n](const size_t k, const size_t v)
246 {
247 return k * n + v;
248 };
249
250 for (size_t v = 0; v < n; ++v)
251 dp[state_of(0, v)] = Cost_Type{0};
252
253 for (size_t k = 1; k <= n; ++k)
254 for (size_t v = 0; v < n; ++v)
255 {
256 Cost_Type best = inf;
257 long best_pred = -1;
258 Arc * best_arc = nullptr;
259
260 for (typename Array<Incoming>::Iterator it(incoming[v]);
261 it.has_curr(); it.next_ne())
262 {
263 const Incoming & in = it.get_curr();
264 const Cost_Type prev = dp[state_of(k - 1, in.src_idx)];
265 if (prev == inf)
266 continue;
267
270 cand,
271 "Karp minimum mean cycle accumulation became non-finite");
272
273 if (best_arc == nullptr
274 or cand < best
275 or (cand == best and in.src_idx < static_cast<size_t>(best_pred)))
276 {
277 best = cand;
278 best_pred = static_cast<long>(in.src_idx);
279 best_arc = in.arc;
280 }
281 }
282
283 dp[state_of(k, v)] = best;
286 }
287
288 const long double ld_inf = std::numeric_limits<long double>::infinity();
289
290 long double best_mean = ld_inf;
291 size_t best_vertex = n;
292
293 for (size_t v = 0; v < n; ++v)
294 {
295 const Cost_Type dnv = dp[state_of(n, v)];
296 if (dnv == inf)
297 continue;
298
299 long double local_max = -ld_inf;
300 bool has_ratio = false;
301
302 for (size_t k = 0; k < n; ++k)
303 {
304 const Cost_Type dkv = dp[state_of(k, v)];
305 if (dkv == inf)
306 continue;
307
308 const long double num = static_cast<long double>(dnv)
309 - static_cast<long double>(dkv);
310 const long double den = static_cast<long double>(n - k);
311 const long double ratio = num / den;
312
313 if (not has_ratio or ratio > local_max)
314 {
315 local_max = ratio;
316 has_ratio = true;
317 }
318 }
319
320 if (not has_ratio)
321 continue;
322
323 result.has_cycle = true;
324
325 if (local_max < best_mean
327 {
329 best_vertex = v;
330 }
331 }
332
333 if (not result.has_cycle)
334 return result;
335
336 result.minimum_mean = best_mean;
337 result.witness_node = nodes[best_vertex];
338
339 // Extract an n-step walk ending at best_vertex from predecessor table.
344
345 size_t curr = best_vertex;
346 reverse_walk_nodes.append(curr);
347
348 for (size_t k = n; k > 0; --k)
349 {
350 const size_t st = state_of(k, curr);
351 const long pred = pred_idx[st];
352 Arc * arc = pred_arc[st];
353
354 if (pred < 0 or arc == nullptr)
355 break;
356
357 reverse_walk_arcs.append(arc);
358 curr = static_cast<size_t>(pred);
359 reverse_walk_nodes.append(curr);
360 }
361
362 if (reverse_walk_nodes.size() < 2)
363 return result;
364
367 for (size_t i = reverse_walk_nodes.size(); i > 0; --i)
368 walk_nodes.append(reverse_walk_nodes[i - 1]);
369
372 for (size_t i = reverse_walk_arcs.size(); i > 0; --i)
373 walk_arcs.append(reverse_walk_arcs[i - 1]);
374
375 const size_t invalid_pos = std::numeric_limits<size_t>::max();
376 Array<size_t> first_pos(n, invalid_pos);
377
378 bool found_cycle = false;
379 size_t best_start = 0;
380 size_t best_end = 0;
382 long double best_cycle_mean = ld_inf;
383
384 for (size_t i = 0; i < walk_nodes.size(); ++i)
385 {
386 const size_t node_idx = walk_nodes[i];
388 << "karp_minimum_mean_cycle(): invalid witness node index";
389
390 const size_t j = first_pos[node_idx];
391 if (j == invalid_pos)
392 {
393 first_pos[node_idx] = i;
394 continue;
395 }
396
397 const size_t len = i - j;
398 if (len == 0)
399 continue;
400
402 for (size_t p = j; p < i; ++p)
403 {
405 cycle_cost, distance(walk_arcs[p]));
408 "Karp minimum mean cycle witness accumulation became non-finite");
409 }
410
411 const long double cycle_mean = static_cast<long double>(cycle_cost)
412 / static_cast<long double>(len);
413
415 {
416 found_cycle = true;
419 best_start = j;
420 best_end = i;
421 }
422 }
423
424 if (not found_cycle)
425 return result;
426
427 result.cycle_total_cost = best_cycle_cost;
428 result.cycle_length = best_end - best_start;
429
430 for (size_t p = best_start; p <= best_end; ++p)
431 result.cycle_nodes.append(nodes[walk_nodes[p]]);
432
433 for (size_t p = best_start; p < best_end; ++p)
434 result.cycle_arcs.append(walk_arcs[p]);
435
436 return result;
437 }
438
446 template <class GT,
447 class Distance = Dft_Dist<GT>,
448 class SA = Dft_Show_Arc<GT>>
451 Distance distance = Distance(),
452 SA sa = SA())
453 {
454 using Node = typename GT::Node;
455 using Arc = typename GT::Arc;
456 using Cost_Type = typename Distance::Distance_Type;
458
460 << "karp_minimum_mean_cycle_value(): graph must be directed";
461
463
465 const size_t n = g.get_num_nodes();
466 if (n == 0)
467 return result;
468
470 n > std::numeric_limits<size_t>::max() - 1
471 or (n + 1) > std::numeric_limits<size_t>::max() / n)
472 << "karp_minimum_mean_cycle_value(): DP table size overflow";
473
474 DynMapTree<Node *, size_t> node_to_idx;
475 size_t idx = 0;
476 for (Node_Iterator<GT> it(g); it.has_curr(); it.next_ne(), ++idx)
477 {
478 node_to_idx.insert(it.get_curr_ne(), idx);
479 }
480
482 incoming.reserve(n);
483 for (size_t i = 0; i < n; ++i)
484 incoming.append(Array<Incoming>());
485
486 for (Arc_Iterator<GT, SA> it(g, sa); it.has_curr(); it.next_ne())
487 {
488 Arc * arc = it.get_curr_ne();
489 Node * src = g.get_src_node(arc);
490 Node * tgt = g.get_tgt_node(arc);
491 const size_t src_idx = node_to_idx.find(src);
492 const size_t tgt_idx = node_to_idx.find(tgt);
493 incoming[tgt_idx].append(Incoming{src_idx, arc, distance(arc)});
494 }
495
496 const Cost_Type inf = std::numeric_limits<Cost_Type>::max();
497 const size_t total_states = (n + 1) * n;
499
500 const auto state_of = [n](const size_t k, const size_t v)
501 {
502 return k * n + v;
503 };
504
505 for (size_t v = 0; v < n; ++v)
506 dp[state_of(0, v)] = Cost_Type{0};
507
508 for (size_t k = 1; k <= n; ++k)
509 for (size_t v = 0; v < n; ++v)
510 {
511 Cost_Type best = inf;
512 bool has_best = false;
513
514 for (typename Array<Incoming>::Iterator it(incoming[v]);
515 it.has_curr(); it.next_ne())
516 {
517 const Incoming & in = it.get_curr();
518 const Cost_Type prev = dp[state_of(k - 1, in.src_idx)];
519 if (prev == inf)
520 continue;
521
524 cand,
525 "Karp minimum mean cycle accumulation became non-finite");
526 if (not has_best or cand < best)
527 {
528 has_best = true;
529 best = cand;
530 }
531 }
532
533 dp[state_of(k, v)] = has_best ? best : inf;
534 }
535
536 const long double ld_inf = std::numeric_limits<long double>::infinity();
537 long double best_mean = ld_inf;
538
539 for (size_t v = 0; v < n; ++v)
540 {
541 const Cost_Type dnv = dp[state_of(n, v)];
542 if (dnv == inf)
543 continue;
544
545 long double local_max = -ld_inf;
546 bool has_ratio = false;
547 for (size_t k = 0; k < n; ++k)
548 {
549 const Cost_Type dkv = dp[state_of(k, v)];
550 if (dkv == inf)
551 continue;
552
553 const long double num = static_cast<long double>(dnv)
554 - static_cast<long double>(dkv);
555 const long double den = static_cast<long double>(n - k);
556 const long double ratio = num / den;
557
558 if (not has_ratio or ratio > local_max)
559 {
560 has_ratio = true;
561 local_max = ratio;
562 }
563 }
564
565 if (not has_ratio)
566 continue;
567
568 result.has_cycle = true;
569 if (local_max < best_mean)
571 }
572
573 if (result.has_cycle)
574 result.minimum_mean = best_mean;
575
576 return result;
577 }
578
583 template <class GT,
584 class Distance = Dft_Dist<GT>,
585 class SA = Dft_Show_Arc<GT>>
588 Distance distance = Distance(),
589 SA sa = SA())
590 {
592 g, std::move(distance), std::move(sa));
593 }
594
595
600 template <class GT,
601 class Distance = Dft_Dist<GT>,
602 class SA = Dft_Show_Arc<GT>>
605 Distance distance = Distance(),
606 SA sa = SA())
607 {
609 g, std::move(distance), std::move(sa));
610 }
611
612
617 template <class GT,
618 class Distance = Dft_Dist<GT>,
619 class SA = Dft_Show_Arc<GT>>
621 {
624
625 public:
627 SA sa = SA())
628 : distance_(std::move(distance)),
629 sa_(std::move(sa))
630 {
631 // empty
632 }
633
635 operator()(const GT & g) const
636 {
638 }
639 };
640
645 template <class GT,
646 class Distance = Dft_Dist<GT>,
647 class SA = Dft_Show_Arc<GT>>
649 {
652
653 public:
655 SA sa = SA())
656 : distance_(std::move(distance)),
657 sa_(std::move(sa))
658 {
659 // empty
660 }
661
663 operator()(const GT & g) const
664 {
666 }
667 };
668} // namespace Aleph
669
670# endif // MIN_MEAN_CYCLE_H
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
WeightedDigraph::Node Node
WeightedDigraph::Arc Arc
List_Graph< Graph_Node< Node_Info >, Graph_Arc< Arc_Info > > GT
long double w
Definition btreepic.C:153
bool has_curr() const noexcept
Check if there is a current valid item.
Definition array_it.H:219
Simple dynamic array with automatic resizing and functional operations.
Definition tpl_array.H:139
void reserve(size_t cap)
Reserves cap cells into the array.
Definition tpl_array.H:315
Default distance accessor for arc weights.
Dynamic singly linked list with functional programming support.
Definition htlist.H:1155
Generic key-value map implemented on top of a binary search tree.
Pair * append(const Key &key, const Data &data)
Pair * insert(const Key &key, const Data &data)
Insert a key-value pair.
Data & find(const Key &key)
Find the value associated with key.
void next_ne() noexcept
Advances the iterator to the next filtered element (noexcept version).
Functor wrapper for value-only minimum mean cycle.
Min_Mean_Cycle_Value_Result operator()(const GT &g) const
Karp_Minimum_Mean_Cycle_Value(Distance distance=Distance(), SA sa=SA())
Functor wrapper for Karp minimum mean cycle.
Min_Mean_Cycle_Result< GT, typename Distance::Distance_Type > operator()(const GT &g) const
Karp_Minimum_Mean_Cycle(Distance distance=Distance(), SA sa=SA())
Filtered iterator on the nodes of a graph.
Definition tpl_graph.H:1206
Node * get_src_node(Arc *arc) const noexcept
Return the source node of arc (only for directed graphs)
Definition graph-dry.H:737
constexpr size_t get_num_nodes() const noexcept
Return the total of nodes of graph.
Definition graph-dry.H:695
bool is_digraph() const noexcept
Return true if the graph this is directed.
Definition graph-dry.H:657
Node * get_tgt_node(Arc *arc) const noexcept
Return the target node of arc (only for directed graphs)
Definition graph-dry.H:743
DynArray< Graph::Node * > nodes
Definition graphpic.C:406
Min_Mean_Cycle_Value_Result minimum_mean_cycle_value(const GT &g, Distance distance=Distance(), SA sa=SA())
Alias for karp_minimum_mean_cycle_value().
Min_Mean_Cycle_Result< GT, typename Distance::Distance_Type > minimum_mean_cycle(const GT &g, Distance distance=Distance(), SA sa=SA())
Alias for karp_minimum_mean_cycle().
Min_Mean_Cycle_Value_Result karp_minimum_mean_cycle_value(const GT &g, Distance distance=Distance(), SA sa=SA())
Compute only the minimum mean value (without witness walk).
Min_Mean_Cycle_Result< GT, typename Distance::Distance_Type > karp_minimum_mean_cycle(const GT &g, Distance distance=Distance(), SA sa=SA())
Compute minimum mean cycle by Karp's algorithm.
Singly linked list implementations with head-tail access.
Freq_Node * pred
Predecessor node in level-order traversal.
void validate_weights(const GT &g, Distance distance, SA sa)
void validate_finite_accumulator(const Cost_Type &value, const char *context)
T checked_add(const T &a, const T &b)
Safely add two distance values with overflow checking.
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.
STL namespace.
Common utilities and base class for shortest path algorithms.
Filtered iterator on all the arcs of a graph.
Definition tpl_graph.H:1164
Iterator on the items of an array.
Definition tpl_array.H:470
Default filter for filtered iterators on arcs.
Definition tpl_graph.H:1000
Result of minimum mean cycle computation.
size_t cycle_length
Number of arcs in witness cycle.
Cost_Type cycle_total_cost
Weight sum of witness cycle.
GT::Node * witness_node
Vertex used in Karp witness extraction.
bool has_cycle
True if at least one directed cycle exists.
DynList< typename GT::Node * > cycle_nodes
Closed witness walk (first node repeated at end).
DynList< typename GT::Arc * > cycle_arcs
Witness arcs aligned with cycle_nodes.
Lightweight minimum-mean-cycle value result.
bool has_cycle
True if at least one directed cycle exists.
Distance accessor.
static int * k
Dynamic array container with automatic resizing.
Dynamic key-value map based on balanced binary search trees.