Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
net_utils_test.cc
Go to the documentation of this file.
1/* Tests for net_utils.H - Network flow utilities
2 *
3 * Tests cover:
4 * - Random network generation
5 * - Grid network generation
6 * - Bipartite network generation
7 * - DOT export
8 * - JSON export/import
9 * - DIMACS export/import
10 * - Benchmarking utilities
11 */
12
13#include <gtest/gtest.h>
14#include <net_utils.H>
15#include <tpl_maxflow.H>
16#include <filesystem>
17#include <fstream>
18#include <string>
19#if defined(_WIN32)
20# include <process.h>
21#else
22# include <unistd.h>
23#endif
24
25using namespace Aleph;
26
27// Network type for tests
30
31class NetUtilsTest : public ::testing::Test
32{
33protected:
34 std::filesystem::path dot_file;
35 std::filesystem::path json_file;
36 std::filesystem::path dimacs_file;
37
38 static long long process_id() noexcept
39 {
40#if defined(_WIN32)
41 return static_cast<long long>(_getpid());
42#else
43 return static_cast<long long>(getpid());
44#endif
45 }
46
47 void SetUp() override
48 {
49 const auto *test_info = ::testing::UnitTest::GetInstance()->current_test_info();
50 std::string base = std::string("alephw_") + test_info->test_suite_name() + "_" +
51 test_info->name() + "_" + std::to_string(process_id());
52
53 for (auto &ch : base)
54 if (ch == '/' || ch == '\\' || ch == ' ')
55 ch = '_';
56
57 auto dir = std::filesystem::temp_directory_path();
58 dot_file = dir / (base + ".dot");
59 json_file = dir / (base + ".json");
60 dimacs_file = dir / (base + ".dimacs");
61 }
62
63 // Cleanup temp files
64 void TearDown() override
65 {
66 std::error_code ec;
67 std::filesystem::remove(dot_file, ec);
68 std::filesystem::remove(json_file, ec);
69 std::filesystem::remove(dimacs_file, ec);
70 }
71};
72
73
74//==============================================================================
75// Random Network Generator Tests
76//==============================================================================
77
79{
81 params.num_nodes = 10;
82 params.num_arcs = 20;
83 params.min_capacity = 1;
84 params.max_capacity = 100;
85 params.seed = 42;
86
88
89 EXPECT_EQ(net.vsize(), 10);
90 EXPECT_GE(net.esize(), params.num_nodes - 1); // At least path arcs
91 // Note: Random networks may have multiple sources/sinks
92 // Use make_super_source/make_super_sink if needed
93}
94
96{
97 auto net = generate_random_network<TestNet>(50, 200, 10, 1000, 123);
98
99 EXPECT_EQ(net.vsize(), 50);
100 EXPECT_GT(net.esize(), 0);
101
102 // Verify capacity bounds
103 for (Arc_Iterator<TestNet> it(net); it.has_curr(); it.next_ne())
104 {
105 auto arc = it.get_curr();
106 EXPECT_GE(arc->cap, 10.0);
107 EXPECT_LE(arc->cap, 1000.0);
108 }
109}
110
112{
114 params.num_nodes = 20;
115 params.num_arcs = 50;
116 params.ensure_connected = true;
117 params.seed = 42;
118
120
121 // Verify connectivity was attempted
122 EXPECT_GE(net.esize(), params.num_nodes - 1);
123}
124
125
126//==============================================================================
127// Grid Network Generator Tests
128//==============================================================================
129
131{
132 // Use unidirectional to ensure single source/sink
133 auto net = generate_grid_network<TestNet>(5, 5, 10.0, false);
134
135 EXPECT_EQ(net.vsize(), 25);
136 EXPECT_TRUE(net.is_single_source());
137 EXPECT_TRUE(net.is_single_sink());
138
139 // Unidirectional: 4*5 + 5*4 = 40 arcs
140 EXPECT_EQ(net.esize(), 40);
141}
142
144{
145 auto net = generate_grid_network<TestNet>(3, 3, 5.0, false);
146
147 EXPECT_EQ(net.vsize(), 9);
148
149 // Unidirectional: 2*3 + 3*2 = 12 arcs
150 EXPECT_EQ(net.esize(), 12);
151}
152
154{
155 // Use unidirectional to ensure single source/sink
156 auto net = generate_grid_network<TestNet>(4, 4, 100.0, false);
157
158 auto flow = dinic_maximum_flow(net);
159 EXPECT_GT(flow, 0.0);
160}
161
162
163//==============================================================================
164// Bipartite Network Generator Tests
165//==============================================================================
166
168{
169 auto net = generate_bipartite_network<TestNet>(10, 10, 0.5, 42);
170
171 // Source + sink + 10 left + 10 right = 22 nodes
172 EXPECT_EQ(net.vsize(), 22);
173 EXPECT_TRUE(net.is_single_source());
174 EXPECT_TRUE(net.is_single_sink());
175}
176
178{
179 auto net = generate_bipartite_network<TestNet>(5, 5, 1.0, 42);
180
181 // With probability 1.0, all edges exist
182 auto flow = dinic_maximum_flow(net);
183
184 // Max matching can be at most min(left, right) = 5
185 EXPECT_LE(flow, 5.0);
186}
187
188
189//==============================================================================
190// Layered Network Generator Tests
191//==============================================================================
192
194{
195 // Use probability 1.0 to ensure all nodes are connected
196 std::vector<size_t> layers = {1, 5, 10, 5, 1};
197 auto net = generate_layered_network<TestNet>(layers, 10.0, 1.0, 42);
198
199 EXPECT_EQ(net.vsize(), 22);
200 EXPECT_GT(net.esize(), 0);
201}
202
204{
205 std::vector<size_t> layers = {1, 3, 3, 1};
206 auto net = generate_layered_network<TestNet>(layers, 10.0, 1.0, 42);
207
208 auto flow = dinic_maximum_flow(net);
209 EXPECT_GT(flow, 0.0);
210}
211
213{
214 std::vector<size_t> layers = {1, 0, 1};
217 std::invalid_argument);
218}
219
220
221//==============================================================================
222// DOT Export Tests
223//==============================================================================
224
226{
227 auto net = generate_grid_network<TestNet>(3, 3, 10.0, false); // unidirectional
229
230 export_network_to_dot(net, dot_file.string());
231
232 std::ifstream file(dot_file);
233 EXPECT_TRUE(file.good());
234
235 std::string content((std::istreambuf_iterator<char>(file)),
236 std::istreambuf_iterator<char>());
237
238 EXPECT_TRUE(content.find("digraph") != std::string::npos);
239 EXPECT_TRUE(content.find("->") != std::string::npos);
240}
241
243{
244 auto net = generate_grid_network<TestNet>(2, 2, 10.0, false); // unidirectional
246
248 options.show_flow = true;
249 options.show_capacity = true;
250 options.highlight_saturated = true;
251
252 export_network_to_dot(net, dot_file.string(), options);
253
254 std::ifstream file(dot_file);
255 EXPECT_TRUE(file.good());
256}
257
259{
260 auto net = generate_grid_network<TestNet>(2, 2, 10.0);
261
262 std::string dot = network_to_dot_string(net);
263
264 EXPECT_TRUE(dot.find("digraph") != std::string::npos);
265 EXPECT_TRUE(dot.find("rankdir=LR") != std::string::npos);
266}
267
268
269//==============================================================================
270// JSON Export Tests
271//==============================================================================
272
274{
275 auto net = generate_grid_network<TestNet>(2, 2, 10.0);
276
277 export_network_to_json(net, json_file.string());
278
279 std::ifstream file(json_file);
280 EXPECT_TRUE(file.good());
281
282 std::string content((std::istreambuf_iterator<char>(file)),
283 std::istreambuf_iterator<char>());
284
285 EXPECT_TRUE(content.find("\"num_nodes\"") != std::string::npos);
286 EXPECT_TRUE(content.find("\"arcs\"") != std::string::npos);
287 EXPECT_TRUE(content.find("\"cap\"") != std::string::npos);
288}
289
291{
292 auto net = generate_grid_network<TestNet>(2, 2, 10.0, false); // unidirectional
293
294 std::string json = network_to_json_string(net);
295
296 EXPECT_TRUE(json.find("\"num_nodes\": 4") != std::string::npos);
297 EXPECT_TRUE(json.find("\"source\"") != std::string::npos);
298 EXPECT_TRUE(json.find("\"sink\"") != std::string::npos);
299}
300
301
302//==============================================================================
303// DIMACS Export/Import Tests
304//==============================================================================
305
307{
308 auto net = generate_grid_network<TestNet>(3, 3, 10.0, false);
309
310 export_network_to_dimacs(net, dimacs_file.string());
311
312 std::ifstream file(dimacs_file);
313 EXPECT_TRUE(file.good());
314
315 std::string content((std::istreambuf_iterator<char>(file)),
316 std::istreambuf_iterator<char>());
317
318 EXPECT_TRUE(content.find("p max 9") != std::string::npos);
319 EXPECT_TRUE(content.find("n") != std::string::npos);
320 EXPECT_TRUE(content.find("a") != std::string::npos);
321}
322
324{
325 // Create and export
326 auto net1 = generate_grid_network<TestNet>(3, 3, 10.0, false);
327 export_network_to_dimacs(net1, dimacs_file.string());
328
329 // Import
330 auto net2 = import_network_from_dimacs<TestNet>(dimacs_file.string());
331
332 EXPECT_EQ(net2.vsize(), net1.vsize());
333 EXPECT_EQ(net2.esize(), net1.esize());
334 EXPECT_TRUE(net2.is_single_source());
335 EXPECT_TRUE(net2.is_single_sink());
336}
337
339{
340 // Create network, compute flow, export
341 auto net1 = generate_grid_network<TestNet>(4, 4, 10.0, false);
343
344 export_network_to_dimacs(net1, dimacs_file.string());
345
346 // Import and compute flow
347 auto net2 = import_network_from_dimacs<TestNet>(dimacs_file.string());
349
350 // Flows should be equal
352}
353
354
355//==============================================================================
356// Benchmark Utilities Tests
357//==============================================================================
358
360{
361 auto net = generate_grid_network<TestNet>(5, 5, 10.0, false); // unidirectional
362
363 auto result = benchmark_maxflow(net, [](auto& n) {
364 return dinic_maximum_flow(n);
365 }, "Dinic");
366
367 EXPECT_GT(result.flow_value, 0.0);
368 EXPECT_GE(result.elapsed_ms, 0.0);
369 EXPECT_EQ(result.algorithm_name, "Dinic");
370}
371
373{
374 std::vector<MaxFlowBenchmarkResult<double>> results;
375
376 results.push_back({100.0, 1.5, "Algorithm A"});
377 results.push_back({100.0, 2.3, "Algorithm B"});
378
379 // Just verify it doesn't crash
380 testing::internal::CaptureStdout();
382 std::string output = testing::internal::GetCapturedStdout();
383
384 EXPECT_TRUE(output.find("Algorithm A") != std::string::npos);
385 EXPECT_TRUE(output.find("Algorithm B") != std::string::npos);
386}
387
388
389//==============================================================================
390// Stress Tests
391//==============================================================================
392
394{
396 params.num_nodes = 100;
397 params.num_arcs = 500;
398 params.seed = 42;
399
401
402 EXPECT_EQ(net.vsize(), 100);
403 EXPECT_GE(net.esize(), 99); // At least connected path
404 // Don't run flow since multi-source/multi-sink
405}
406
408{
409 auto net = generate_grid_network<TestNet>(20, 20, 100.0, false); // unidirectional
410
411 EXPECT_EQ(net.vsize(), 400);
412
413 // Should complete in reasonable time
414 auto flow = dinic_maximum_flow(net);
415 EXPECT_GT(flow, 0.0);
416}
417
418
419int main(int argc, char** argv)
420{
421 ::testing::InitGoogleTest(&argc, argv);
422 return RUN_ALL_TESTS();
423}
void next_ne() noexcept
Advances the iterator to the next filtered element (noexcept version).
std::filesystem::path json_file
static long long process_id() noexcept
std::filesystem::path dot_file
void TearDown() override
std::filesystem::path dimacs_file
void SetUp() override
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
void export_network_to_dimacs(const Net &net, const std::string &filename)
Export network to DIMACS max-flow format.
Definition net_utils.H:820
void export_network_to_dot(const Net &net, const std::string &filename, const DotExportOptions &options=DotExportOptions())
Export network to DOT format for GraphViz visualization.
Definition net_utils.H:484
Net::Flow_Type dinic_maximum_flow(Net &net)
Compute maximum flow using Dinic's algorithm.
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.
void export_network_to_json(const Net &net, const std::string &filename)
Export network to JSON format.
Definition net_utils.H:694
std::string network_to_json_string(const Net &net)
Export network to JSON string.
Definition net_utils.H:750
MaxFlowBenchmarkResult< typename Net::Flow_Type > benchmark_maxflow(Net &net, Algo algo, const std::string &name)
Run and time a max-flow algorithm.
Definition net_utils.H:957
void print_benchmark_results(const std::vector< MaxFlowBenchmarkResult< Flow_Type > > &results)
Print benchmark results.
Definition net_utils.H:976
std::string network_to_dot_string(const Net &net, const DotExportOptions &options=DotExportOptions())
Generate DOT string for network (instead of file).
Definition net_utils.H:579
Network flow utilities: generators, visualization, serialization.
TEST_F(NetUtilsTest, GenerateRandomNetworkBasic)
static struct argp_option options[]
Definition ntreepic.C:1886
Filtered iterator on all the arcs of a graph.
Definition tpl_graph.H:1164
Options for DOT export.
Definition net_utils.H:449
bool show_flow
Show flow values on arcs.
Definition net_utils.H:450
Arc of a flow network implemented with adjacency lists.
Definition tpl_net.H:115
Flow network implemented with adjacency lists.
Definition tpl_net.H:261
Parameters for random network generation.
Definition net_utils.H:101
size_t num_nodes
Number of nodes.
Definition net_utils.H:102
Advanced maximum flow algorithms.
ofstream output
Definition writeHeap.C:215
ofstream file
Definition writeRb.C:46