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
64# ifndef MIN_MEAN_CYCLE_H
65# define MIN_MEAN_CYCLE_H
66
67# include <ah-graph-concepts.H>
68
69# include <cmath>
70# include <cstddef>
71# include <limits>
72# include <type_traits>
73# include <utility>
74
75# include <ah-errors.H>
76# include <shortest_path_common.H>
77# include <htlist.H>
78# include <tpl_array.H>
79# include <tpl_dynMapTree.H>
80
81namespace Aleph
82{
89 template <AlephGraph GT, typename Cost_Type>
91 {
92 bool has_cycle = false;
93 long double minimum_mean = std::numeric_limits<long double>::infinity();
94 typename GT::Node * witness_node = nullptr;
95
97 size_t cycle_length = 0;
98
101 };
102
111 {
112 bool has_cycle = false;
113 long double minimum_mean = std::numeric_limits<long double>::infinity();
114 };
115
116
117 namespace min_mean_cycle_detail
118 {
119 template <AlephGraph GT, typename Cost_Type>
121 {
122 size_t src_idx = 0;
123 typename GT::Arc * arc = nullptr;
125 };
126
127
128 template <AlephGraph GT, ArcDistance<GT> Distance, ArcFilter<GT> SA>
129 void validate_weights(const GT & g, Distance distance, SA sa)
130 {
131 using Arc = typename GT::Arc;
132 using Cost_Type = typename Distance::Distance_Type;
133
134 static_assert(std::is_arithmetic_v<Cost_Type>,
135 "Karp minimum mean cycle requires arithmetic arc costs");
136
137 for (Arc_Iterator<GT, SA> it(g, sa); it.has_curr(); it.next_ne())
138 {
139 Arc * arc = it.get_curr_ne();
140 const Cost_Type w = distance(arc);
141
142 if constexpr (std::is_floating_point_v<Cost_Type>)
143 ah_domain_error_if(not std::isfinite(w))
144 << "Karp minimum mean cycle requires finite arc weights";
145 }
146 }
147
148
149 template <typename Cost_Type>
150 void validate_finite_accumulator(const Cost_Type & value, const char * context)
151 {
152 if constexpr (std::is_floating_point_v<Cost_Type>)
153 ah_domain_error_if(not std::isfinite(value))
154 << context;
155 }
156 } // namespace min_mean_cycle_detail
157
158
182 template <AlephGraph GT,
187 Distance distance = Distance(),
188 SA sa = SA())
189 {
190 using Node = typename GT::Node;
191 using Arc = typename GT::Arc;
192 using Cost_Type = typename Distance::Distance_Type;
195
197 << "karp_minimum_mean_cycle(): graph must be directed";
198
200
201 Result result;
202 const size_t n = g.get_num_nodes();
203 if (n == 0)
204 return result;
205
207 n > std::numeric_limits<size_t>::max() - 1
208 or (n + 1) > std::numeric_limits<size_t>::max() / n)
209 << "karp_minimum_mean_cycle(): DP table size overflow";
210
212 nodes.reserve(n);
213
214 DynMapTree<Node *, size_t> node_to_idx;
215
216 size_t idx = 0;
217 for (Node_Iterator<GT> it(g); it.has_curr(); it.next_ne(), ++idx)
218 {
219 Node * node = it.get_curr_ne();
220 nodes.append(node);
221 node_to_idx.insert(node, idx);
222 }
223
225 incoming.reserve(n);
226 for (size_t i = 0; i < n; ++i)
227 incoming.append(Array<Incoming>());
228
229 for (Arc_Iterator<GT, SA> it(g, sa); it.has_curr(); it.next_ne())
230 {
231 Arc * arc = it.get_curr_ne();
232 Node * src = g.get_src_node(arc);
233 Node * tgt = g.get_tgt_node(arc);
234
235 const size_t src_idx = node_to_idx.find(src);
236 const size_t tgt_idx = node_to_idx.find(tgt);
237
238 incoming[tgt_idx].append(Incoming{src_idx, arc, distance(arc)});
239 }
240
241 const Cost_Type inf = std::numeric_limits<Cost_Type>::max();
242 const size_t total_states = (n + 1) * n;
243
247
248 const auto state_of = [n](const size_t k, const size_t v)
249 {
250 return k * n + v;
251 };
252
253 for (size_t v = 0; v < n; ++v)
254 dp[state_of(0, v)] = Cost_Type{0};
255
256 for (size_t k = 1; k <= n; ++k)
257 for (size_t v = 0; v < n; ++v)
258 {
259 Cost_Type best = inf;
260 long best_pred = -1;
261 Arc * best_arc = nullptr;
262
263 for (typename Array<Incoming>::Iterator it(incoming[v]);
264 it.has_curr(); it.next_ne())
265 {
266 const Incoming & in = it.get_curr();
267 const Cost_Type prev = dp[state_of(k - 1, in.src_idx)];
268 if (prev == inf)
269 continue;
270
273 cand,
274 "Karp minimum mean cycle accumulation became non-finite");
275
276 if (best_arc == nullptr
277 or cand < best
278 or (cand == best and in.src_idx < static_cast<size_t>(best_pred)))
279 {
280 best = cand;
281 best_pred = static_cast<long>(in.src_idx);
282 best_arc = in.arc;
283 }
284 }
285
286 dp[state_of(k, v)] = best;
289 }
290
291 const long double ld_inf = std::numeric_limits<long double>::infinity();
292
293 long double best_mean = ld_inf;
294 size_t best_vertex = n;
295
296 for (size_t v = 0; v < n; ++v)
297 {
298 const Cost_Type dnv = dp[state_of(n, v)];
299 if (dnv == inf)
300 continue;
301
302 long double local_max = -ld_inf;
303 bool has_ratio = false;
304
305 for (size_t k = 0; k < n; ++k)
306 {
307 const Cost_Type dkv = dp[state_of(k, v)];
308 if (dkv == inf)
309 continue;
310
311 const long double num = static_cast<long double>(dnv)
312 - static_cast<long double>(dkv);
313 const long double den = static_cast<long double>(n - k);
314 const long double ratio = num / den;
315
316 if (not has_ratio or ratio > local_max)
317 {
318 local_max = ratio;
319 has_ratio = true;
320 }
321 }
322
323 if (not has_ratio)
324 continue;
325
326 result.has_cycle = true;
327
328 if (local_max < best_mean
330 {
332 best_vertex = v;
333 }
334 }
335
336 if (not result.has_cycle)
337 return result;
338
339 result.minimum_mean = best_mean;
340 result.witness_node = nodes[best_vertex];
341
342 // Extract an n-step walk ending at best_vertex from predecessor table.
347
348 size_t curr = best_vertex;
349 reverse_walk_nodes.append(curr);
350
351 for (size_t k = n; k > 0; --k)
352 {
353 const size_t st = state_of(k, curr);
354 const long pred = pred_idx[st];
355 Arc * arc = pred_arc[st];
356
357 if (pred < 0 or arc == nullptr)
358 break;
359
360 reverse_walk_arcs.append(arc);
361 curr = static_cast<size_t>(pred);
362 reverse_walk_nodes.append(curr);
363 }
364
365 if (reverse_walk_nodes.size() < 2)
366 return result;
367
370 for (size_t i = reverse_walk_nodes.size(); i > 0; --i)
371 walk_nodes.append(reverse_walk_nodes[i - 1]);
372
375 for (size_t i = reverse_walk_arcs.size(); i > 0; --i)
376 walk_arcs.append(reverse_walk_arcs[i - 1]);
377
378 const size_t invalid_pos = std::numeric_limits<size_t>::max();
379 Array<size_t> first_pos(n, invalid_pos);
380
381 bool found_cycle = false;
382 size_t best_start = 0;
383 size_t best_end = 0;
385 long double best_cycle_mean = ld_inf;
386
387 for (size_t i = 0; i < walk_nodes.size(); ++i)
388 {
389 const size_t node_idx = walk_nodes[i];
391 << "karp_minimum_mean_cycle(): invalid witness node index";
392
393 const size_t j = first_pos[node_idx];
394 if (j == invalid_pos)
395 {
396 first_pos[node_idx] = i;
397 continue;
398 }
399
400 const size_t len = i - j;
401 if (len == 0)
402 continue;
403
405 for (size_t p = j; p < i; ++p)
406 {
408 cycle_cost, distance(walk_arcs[p]));
411 "Karp minimum mean cycle witness accumulation became non-finite");
412 }
413
414 const long double cycle_mean = static_cast<long double>(cycle_cost)
415 / static_cast<long double>(len);
416
418 {
419 found_cycle = true;
422 best_start = j;
423 best_end = i;
424 }
425 }
426
427 if (not found_cycle)
428 return result;
429
430 result.cycle_total_cost = best_cycle_cost;
431 result.cycle_length = best_end - best_start;
432
433 for (size_t p = best_start; p <= best_end; ++p)
434 result.cycle_nodes.append(nodes[walk_nodes[p]]);
435
436 for (size_t p = best_start; p < best_end; ++p)
437 result.cycle_arcs.append(walk_arcs[p]);
438
439 return result;
440 }
441
449 template <AlephGraph GT,
454 Distance distance = Distance(),
455 SA sa = SA())
456 {
457 using Node = typename GT::Node;
458 using Arc = typename GT::Arc;
459 using Cost_Type = typename Distance::Distance_Type;
461
463 << "karp_minimum_mean_cycle_value(): graph must be directed";
464
466
468 const size_t n = g.get_num_nodes();
469 if (n == 0)
470 return result;
471
473 n > std::numeric_limits<size_t>::max() - 1
474 or (n + 1) > std::numeric_limits<size_t>::max() / n)
475 << "karp_minimum_mean_cycle_value(): DP table size overflow";
476
477 DynMapTree<Node *, size_t> node_to_idx;
478 size_t idx = 0;
479 for (Node_Iterator<GT> it(g); it.has_curr(); it.next_ne(), ++idx)
480 {
481 node_to_idx.insert(it.get_curr_ne(), idx);
482 }
483
485 incoming.reserve(n);
486 for (size_t i = 0; i < n; ++i)
487 incoming.append(Array<Incoming>());
488
489 for (Arc_Iterator<GT, SA> it(g, sa); it.has_curr(); it.next_ne())
490 {
491 Arc * arc = it.get_curr_ne();
492 Node * src = g.get_src_node(arc);
493 Node * tgt = g.get_tgt_node(arc);
494 const size_t src_idx = node_to_idx.find(src);
495 const size_t tgt_idx = node_to_idx.find(tgt);
496 incoming[tgt_idx].append(Incoming{src_idx, arc, distance(arc)});
497 }
498
499 const Cost_Type inf = std::numeric_limits<Cost_Type>::max();
500 const size_t total_states = (n + 1) * n;
502
503 const auto state_of = [n](const size_t k, const size_t v)
504 {
505 return k * n + v;
506 };
507
508 for (size_t v = 0; v < n; ++v)
509 dp[state_of(0, v)] = Cost_Type{0};
510
511 for (size_t k = 1; k <= n; ++k)
512 for (size_t v = 0; v < n; ++v)
513 {
514 Cost_Type best = inf;
515 bool has_best = false;
516
517 for (typename Array<Incoming>::Iterator it(incoming[v]);
518 it.has_curr(); it.next_ne())
519 {
520 const Incoming & in = it.get_curr();
521 const Cost_Type prev = dp[state_of(k - 1, in.src_idx)];
522 if (prev == inf)
523 continue;
524
527 cand,
528 "Karp minimum mean cycle accumulation became non-finite");
529 if (not has_best or cand < best)
530 {
531 has_best = true;
532 best = cand;
533 }
534 }
535
536 dp[state_of(k, v)] = has_best ? best : inf;
537 }
538
539 const long double ld_inf = std::numeric_limits<long double>::infinity();
540 long double best_mean = ld_inf;
541
542 for (size_t v = 0; v < n; ++v)
543 {
544 const Cost_Type dnv = dp[state_of(n, v)];
545 if (dnv == inf)
546 continue;
547
548 long double local_max = -ld_inf;
549 bool has_ratio = false;
550 for (size_t k = 0; k < n; ++k)
551 {
552 const Cost_Type dkv = dp[state_of(k, v)];
553 if (dkv == inf)
554 continue;
555
556 const long double num = static_cast<long double>(dnv)
557 - static_cast<long double>(dkv);
558 const long double den = static_cast<long double>(n - k);
559 const long double ratio = num / den;
560
561 if (not has_ratio or ratio > local_max)
562 {
563 has_ratio = true;
564 local_max = ratio;
565 }
566 }
567
568 if (not has_ratio)
569 continue;
570
571 result.has_cycle = true;
572 if (local_max < best_mean)
574 }
575
576 if (result.has_cycle)
577 result.minimum_mean = best_mean;
578
579 return result;
580 }
581
586 template <AlephGraph GT,
591 Distance distance = Distance(),
592 SA sa = SA())
593 {
595 g, std::move(distance), std::move(sa));
596 }
597
598
603 template <AlephGraph GT,
608 Distance distance = Distance(),
609 SA sa = SA())
610 {
612 g, std::move(distance), std::move(sa));
613 }
614
615
620 template <AlephGraph GT,
624 {
626 SA sa_;
627
628 public:
630 SA sa = SA())
631 : distance_(std::move(distance)),
632 sa_(std::move(sa))
633 {
634 // empty
635 }
636
638 operator()(const GT & g) const
639 {
641 }
642 };
643
648 template <AlephGraph GT,
652 {
654 SA sa_;
655
656 public:
658 SA sa = SA())
659 : distance_(std::move(distance)),
660 sa_(std::move(sa))
661 {
662 // empty
663 }
664
666 operator()(const GT & g) const
667 {
669 }
670 };
671} // namespace Aleph
672
673# 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: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.
WeightedDigraph::Node Node
WeightedDigraph::Arc Arc
List_Graph< Graph_Node< Node_Info >, Graph_Arc< Arc_Info > > GT
long double w
Definition btreepic.C:153
size_t size_t int32_t value
Definition ca-c-api.h:116
bool has_curr() const noexcept
Check if there is a current valid item.
Definition array_it.H:231
Simple dynamic array with automatic resizing and functional operations.
Definition tpl_array.H:138
void reserve(size_t cap)
Reserves cap cells into the array.
Definition tpl_array.H:320
Default distance accessor for arc weights.
Doubly-linked list (defined in tpl_dynList.H).
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:1207
Node * get_src_node(Arc *arc) const noexcept
Return the source node of arc (only for directed graphs)
Definition graph-dry.H:779
constexpr size_t get_num_nodes() const noexcept
Return the total of nodes of graph.
Definition graph-dry.H:737
bool is_digraph() const noexcept
Return true if the graph this is directed.
Definition graph-dry.H:699
Node * get_tgt_node(Arc *arc) const noexcept
Return the target node of arc (only for directed graphs)
Definition graph-dry.H:785
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.
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
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.
STL namespace.
Common utilities and base class for shortest path algorithms.
Filtered iterator on all the arcs of a graph.
Definition tpl_graph.H:1165
Iterator on the items of an array.
Definition tpl_array.H:608
Default filter for filtered iterators on arcs.
Definition tpl_graph.H:1001
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.