Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
min_mean_cycle_test.cc
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
37# include <gtest/gtest.h>
38
39# include <cmath>
40# include <functional>
41# include <limits>
42# include <random>
43# include <tuple>
44# include <vector>
45
46# include <Min_Mean_Cycle.H>
47# include <tpl_agraph.H>
48# include <tpl_graph.H>
49
50using namespace Aleph;
51
52namespace
53{
58 using Node = Graph::Node;
59 using Arc = Graph::Arc;
60 using Edge_Def = std::tuple<size_t, size_t, long long>;
61 using Float_Edge_Def = std::tuple<size_t, size_t, double>;
63
64 template <class GT>
65 struct Built_Graph_T
66 {
67 GT g;
68 std::vector<typename GT::Node *> nodes;
69 std::vector<typename GT::Arc *> arcs;
70 };
71
72 using Built_Graph = Built_Graph_T<Graph>;
75
76 template <class GT, typename Weight_Type>
78 build_graph_generic(const size_t n, const std::vector<std::tuple<size_t, size_t, Weight_Type>> & edges)
79 {
81 built.nodes.reserve(n);
82 built.arcs.reserve(edges.size());
83
84 for (size_t i = 0; i < n; ++i)
85 built.nodes.push_back(built.g.insert_node(static_cast<int>(i)));
86
87 for (const auto & [u, v, w] : edges)
88 if (u < n and v < n)
89 built.arcs.push_back(built.g.insert_arc(built.nodes[u], built.nodes[v], w));
90
91 return built;
92 }
93
94 Built_Graph build_graph(const size_t n, const std::vector<Edge_Def> & edges)
95 {
97 }
98
99 Built_Float_Graph build_float_graph(const size_t n, const std::vector<Float_Edge_Def> & edges)
100 {
102 }
103
104 Built_Array_Graph build_array_graph(const size_t n, const std::vector<Edge_Def> & edges)
105 {
107 }
108
109
110 struct Exact_Oracle_Result
111 {
112 bool has_cycle = false;
113 long double minimum_mean = std::numeric_limits<long double>::infinity();
114 };
115
116
117 Exact_Oracle_Result exact_minimum_mean_cycle(const Built_Graph & built)
118 {
119 const size_t n = built.nodes.size();
120
121 Exact_Oracle_Result oracle;
122 std::vector<bool> visited(n, false);
123
124 std::function<void(size_t, size_t, long long, size_t)> dfs =
125 [&](const size_t start,
126 const size_t u,
127 const long long cost,
128 const size_t len)
129 {
130 for (Node_Arc_Iterator<Graph> it(built.nodes[u]); it.has_curr(); it.next_ne())
131 {
132 Arc * arc = it.get_current_arc_ne();
133 const size_t v = static_cast<size_t>(it.get_tgt_node()->get_info());
134 const long long w = arc->get_info();
135
136 if (v == start)
137 {
138 const size_t cyc_len = len + 1;
139 const long double mean = static_cast<long double>(cost + w)
140 / static_cast<long double>(cyc_len);
141 oracle.has_cycle = true;
142 if (mean < oracle.minimum_mean)
143 oracle.minimum_mean = mean;
144 continue;
145 }
146
147 if (visited[v])
148 continue;
149
150 visited[v] = true;
151 dfs(start, v, cost + w, len + 1);
152 visited[v] = false;
153 }
154 };
155
156 for (size_t s = 0; s < n; ++s)
157 {
158 visited[s] = true;
159 dfs(s, s, 0, 0);
160 visited[s] = false;
161 }
162
163 return oracle;
164 }
165
166
167 bool witness_cycle_is_consistent(const Graph & g, const Result & r)
168 {
169 if (r.cycle_length == 0)
170 return r.cycle_nodes.is_empty() and r.cycle_arcs.is_empty();
171
172 if (r.cycle_nodes.size() != r.cycle_length + 1)
173 return false;
174 if (r.cycle_arcs.size() != r.cycle_length)
175 return false;
176
177 auto node_it = r.cycle_nodes.get_it();
178 if (not node_it.has_curr())
179 return false;
180
181 Node * first = node_it.get_curr();
182 Node * curr = first;
183 node_it.next_ne();
184
185 for (auto arc_it = r.cycle_arcs.get_it(); arc_it.has_curr(); arc_it.next_ne())
186 {
187 if (not node_it.has_curr())
188 return false;
189
190 Arc * arc = arc_it.get_curr();
191 Node * next = node_it.get_curr();
192
193 if (g.get_src_node(arc) != curr or g.get_tgt_node(arc) != next)
194 return false;
195
196 curr = next;
197 node_it.next_ne();
198 }
199
200 return curr == first and not node_it.has_curr();
201 }
202
203
204 struct Hide_Arc
205 {
206 Arc * blocked = nullptr;
207
208 bool operator()(Arc * arc) const noexcept
209 {
210 return arc != blocked;
211 }
212 };
213} // namespace
214
215
217{
218 Graph g;
219 const auto r = karp_minimum_mean_cycle(g);
220
221 EXPECT_FALSE(r.has_cycle);
222 EXPECT_EQ(r.cycle_length, 0u);
223 EXPECT_TRUE(r.cycle_nodes.is_empty());
224 EXPECT_TRUE(r.cycle_arcs.is_empty());
225}
226
227
229{
230 const std::vector<Edge_Def> edges = {
231 {0, 1, 2}, {1, 2, 3}, {2, 3, 4}, {0, 3, 10}
232 };
233
234 auto built = build_graph(4, edges);
235 const auto r = karp_minimum_mean_cycle(built.g);
236
237 EXPECT_FALSE(r.has_cycle);
238 EXPECT_EQ(r.cycle_length, 0u);
239}
240
241
243{
244 const std::vector<Edge_Def> edges = {
245 {0, 1, 8}, {1, 1, 3}, {1, 2, 7}, {2, 0, 9}
246 };
247
248 auto built = build_graph(3, edges);
249 const auto r = karp_minimum_mean_cycle(built.g);
250
251 ASSERT_TRUE(r.has_cycle);
252 EXPECT_NEAR(static_cast<double>(r.minimum_mean), 3.0, 1e-12);
253 EXPECT_EQ(r.cycle_length, 1u);
254 EXPECT_EQ(r.cycle_nodes.size(), 2u);
255 EXPECT_EQ(r.cycle_total_cost, 3);
257}
258
259
261{
262 const std::vector<Edge_Def> edges = {
263 {0, 1, 3}, {1, 0, 1}, // mean 2
264 {1, 2, 1}, {2, 1, 1}, // mean 1 (optimal)
265 {0, 2, 5}, {2, 0, 5}
266 };
267
268 auto built = build_graph(3, edges);
269 const auto r = karp_minimum_mean_cycle(built.g);
270
271 ASSERT_TRUE(r.has_cycle);
272 EXPECT_NEAR(static_cast<double>(r.minimum_mean), 1.0, 1e-12);
274}
275
276
278{
279 const std::vector<Edge_Def> edges = {
280 {0, 1, -5}, {1, 2, 1}, {2, 0, 1}, // mean -1 (optimal)
281 {0, 3, 4}, {3, 0, 4}
282 };
283
284 auto built = build_graph(4, edges);
285 const auto r = karp_minimum_mean_cycle(built.g);
286
287 ASSERT_TRUE(r.has_cycle);
288 EXPECT_NEAR(static_cast<double>(r.minimum_mean), -1.0, 1e-12);
290}
291
292
294{
295 const std::vector<Edge_Def> edges = {
296 {0, 1, 1}, {1, 0, 1}, // mean 1 (will be blocked)
297 {2, 3, 3}, {3, 2, 3} // mean 3
298 };
299
300 auto built = build_graph(4, edges);
301
302 const auto full = karp_minimum_mean_cycle(built.g);
303 ASSERT_TRUE(full.has_cycle);
304 EXPECT_NEAR(static_cast<double>(full.minimum_mean), 1.0, 1e-12);
305
306 const Hide_Arc filter{built.arcs[0]};
309
310 ASSERT_TRUE(filtered.has_cycle);
311 EXPECT_NEAR(static_cast<double>(filtered.minimum_mean), 3.0, 1e-12);
313}
314
316{
317 const std::vector<Float_Edge_Def> edges = {
318 {0, 1, 0.5}, {1, 0, 1.0}, // mean 0.75
319 {1, 2, 2.5}, {2, 1, 2.5} // mean 2.5
320 };
321
322 auto built = build_float_graph(3, edges);
323 const auto full = karp_minimum_mean_cycle(built.g);
324 const auto value = karp_minimum_mean_cycle_value(built.g);
327
328 ASSERT_TRUE(full.has_cycle);
329 EXPECT_NEAR(static_cast<double>(full.minimum_mean), 0.75, 1e-12);
330
331 ASSERT_TRUE(value.has_cycle);
332 EXPECT_NEAR(static_cast<double>(value.minimum_mean), 0.75, 1e-12);
333
334 ASSERT_TRUE(alias_value.has_cycle);
335 EXPECT_NEAR(static_cast<double>(alias_value.minimum_mean), 0.75, 1e-12);
336
337 ASSERT_TRUE(value_functor.has_cycle);
338 EXPECT_NEAR(static_cast<double>(value_functor.minimum_mean), 0.75, 1e-12);
339}
340
342{
344 auto * i0 = inf_graph.insert_node(0);
345 auto * i1 = inf_graph.insert_node(1);
346 inf_graph.insert_arc(i0, i1, std::numeric_limits<double>::infinity());
347 inf_graph.insert_arc(i1, i0, 1.0);
348
349 EXPECT_THROW((karp_minimum_mean_cycle(inf_graph)), std::domain_error);
351
353 auto * n0 = nan_graph.insert_node(0);
354 auto * n1 = nan_graph.insert_node(1);
355 nan_graph.insert_arc(n0, n1, std::numeric_limits<double>::quiet_NaN());
356 nan_graph.insert_arc(n1, n0, 1.0);
357
358 EXPECT_THROW((karp_minimum_mean_cycle(nan_graph)), std::domain_error);
360}
361
363{
364 const std::vector<Edge_Def> edges = {
365 {0, 1, std::numeric_limits<long long>::max()},
366 {1, 0, 1}
367 };
368
369 auto built = build_graph(2, edges);
370 EXPECT_THROW((karp_minimum_mean_cycle(built.g)), std::overflow_error);
371 EXPECT_THROW((karp_minimum_mean_cycle_value(built.g)), std::overflow_error);
372}
373
375{
376 const std::vector<Edge_Def> edges = {
377 {0, 1, 3}, {1, 2, 3}, {2, 0, 0}, // mean 2
378 {0, 3, 5}, {3, 0, 5}
379 };
380
381 auto built = build_array_graph(4, edges);
382 const auto r = karp_minimum_mean_cycle(built.g);
383 const auto v = karp_minimum_mean_cycle_value(built.g);
384
385 ASSERT_TRUE(r.has_cycle);
386 EXPECT_NEAR(static_cast<double>(r.minimum_mean), 2.0, 1e-12);
387 ASSERT_TRUE(v.has_cycle);
388 EXPECT_NEAR(static_cast<double>(v.minimum_mean), 2.0, 1e-12);
389}
390
391
393{
394 const std::vector<Edge_Def> edges = {
395 {0, 1, 2}, {1, 0, 2},
396 {1, 2, 1}, {2, 1, 1}
397 };
398
399 auto built = build_graph(3, edges);
400
402 const auto alias_result = minimum_mean_cycle(built.g);
404
405 ASSERT_EQ(free_result.has_cycle, alias_result.has_cycle);
406 ASSERT_EQ(free_result.has_cycle, functor_result.has_cycle);
407 if (free_result.has_cycle)
408 {
409 EXPECT_NEAR(static_cast<double>(free_result.minimum_mean),
410 static_cast<double>(alias_result.minimum_mean),
411 1e-12);
412 EXPECT_NEAR(static_cast<double>(free_result.minimum_mean),
413 static_cast<double>(functor_result.minimum_mean),
414 1e-12);
415 }
416}
417
418
420{
421 UGraph g;
422 auto * n0 = g.insert_node(0);
423 auto * n1 = g.insert_node(1);
424 g.insert_arc(n0, n1, 1);
425
426 EXPECT_THROW((karp_minimum_mean_cycle(g)), std::domain_error);
427}
428
429
431{
432 std::mt19937_64 rng(0xDA7A5EEDULL);
433 std::uniform_int_distribution<int> n_dist(2, 7);
434 std::bernoulli_distribution has_edge(0.36);
435 std::uniform_int_distribution<int> weight_dist(-9, 13);
436
437 for (size_t trial = 0; trial < 90; ++trial)
438 {
439 const size_t n = static_cast<size_t>(n_dist(rng));
440 std::vector<Edge_Def> edges;
441 for (size_t u = 0; u < n; ++u)
442 for (size_t v = 0; v < n; ++v)
443 {
444 if (u == v)
445 {
446 if (std::bernoulli_distribution(0.12)(rng))
447 edges.emplace_back(u, v, static_cast<long long>(weight_dist(rng)));
448 continue;
449 }
450
451 if (has_edge(rng))
452 edges.emplace_back(u, v, static_cast<long long>(weight_dist(rng)));
453 }
454
455 auto built = build_graph(n, edges);
457 const auto got = karp_minimum_mean_cycle(built.g);
459
460 ASSERT_EQ(got.has_cycle, exact.has_cycle) << "trial=" << trial;
461 ASSERT_EQ(value_only.has_cycle, exact.has_cycle) << "trial=" << trial;
462 if (not got.has_cycle)
463 continue;
464
465 EXPECT_NEAR(static_cast<double>(got.minimum_mean),
466 static_cast<double>(exact.minimum_mean),
467 1e-10)
468 << "trial=" << trial;
469 EXPECT_NEAR(static_cast<double>(value_only.minimum_mean),
470 static_cast<double>(exact.minimum_mean),
471 1e-10)
472 << "trial=" << trial;
473
474 EXPECT_GT(got.cycle_length, 0u) << "trial=" << trial;
476
477 const long double witness_mean = static_cast<long double>(got.cycle_total_cost)
478 / static_cast<long double>(got.cycle_length);
479 EXPECT_GE(witness_mean + 1e-12L, got.minimum_mean) << "trial=" << trial;
480 }
481}
Minimum mean cycle in directed graphs (Karp algorithm).
WeightedDigraph::Node Node
WeightedDigraph::Arc Arc
long double w
Definition btreepic.C:153
Generic directed graph (digraph) wrapper template.
Definition graph-dry.H:3854
void next_ne() noexcept
Advances the iterator to the next filtered element (noexcept version).
virtual Node * insert_node(Node *node) noexcept
Insertion of a node already allocated.
Definition tpl_graph.H:524
Node Node
The graph type.
Definition tpl_graph.H:432
Arc Arc
The node class type.
Definition tpl_graph.H:433
Arc * insert_arc(Node *src_node, Node *tgt_node, void *a)
Definition tpl_graph.H:604
Node * get_src_node(Arc *arc) const noexcept
Return the source node of arc (only for directed graphs)
Definition graph-dry.H:737
Node * get_tgt_node(Arc *arc) const noexcept
Return the target node of arc (only for directed graphs)
Definition graph-dry.H:743
#define TEST(name)
static mt19937 rng
DynArray< Graph::Node * > nodes
Definition graphpic.C:406
DynArray< Graph::Arc * > arcs
Definition graphpic.C:408
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().
bool has_cycle(const GT &g)
Return true if an undirected graph has at least one cycle.
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.
DFSResult< typename Domain::Distance > dfs(Domain &domain, typename Domain::State &state, SearchPath< typename Domain::Move > &path, const typename Domain::Distance g, const typename Domain::Distance threshold, const size_t depth, IDAStarResult< Solution, typename Domain::Distance > &result, OnSolution &on_solution)
Core recursive DFS used by IDA* for a single threshold pass.
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
and
Check uniqueness with explicit hash + equality functors.
Container2< typename Container1::Item_Type > filter(Container1 &container, Operation &operation)
Filter elements that satisfy operation.
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.
auto mean(const Container &data) -> std::decay_t< decltype(*std::begin(data))>
Compute the arithmetic mean.
Definition stat_utils.H:183
void next()
Advance all underlying iterators (bounds-checked).
Definition ah-zip.H:171
Arc of graph implemented with double-linked adjacency lists.
Definition tpl_graph.H:222
Result of minimum mean cycle computation.
Filtered iterator of adjacent arcs of a node.
Definition tpl_graph.H:1119
gsl_rng * r
Array-based graph implementation.
Generic graph and digraph implementations.