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
212
213//==============================================================================
214// DOT Export Tests
215//==============================================================================
216
218{
219 auto net = generate_grid_network<TestNet>(3, 3, 10.0, false); // unidirectional
221
222 export_network_to_dot(net, dot_file.string());
223
224 std::ifstream file(dot_file);
225 EXPECT_TRUE(file.good());
226
227 std::string content((std::istreambuf_iterator<char>(file)),
228 std::istreambuf_iterator<char>());
229
230 EXPECT_TRUE(content.find("digraph") != std::string::npos);
231 EXPECT_TRUE(content.find("->") != std::string::npos);
232}
233
235{
236 auto net = generate_grid_network<TestNet>(2, 2, 10.0, false); // unidirectional
238
240 options.show_flow = true;
241 options.show_capacity = true;
242 options.highlight_saturated = true;
243
244 export_network_to_dot(net, dot_file.string(), options);
245
246 std::ifstream file(dot_file);
247 EXPECT_TRUE(file.good());
248}
249
251{
252 auto net = generate_grid_network<TestNet>(2, 2, 10.0);
253
254 std::string dot = network_to_dot_string(net);
255
256 EXPECT_TRUE(dot.find("digraph") != std::string::npos);
257 EXPECT_TRUE(dot.find("rankdir=LR") != std::string::npos);
258}
259
260
261//==============================================================================
262// JSON Export Tests
263//==============================================================================
264
266{
267 auto net = generate_grid_network<TestNet>(2, 2, 10.0);
268
269 export_network_to_json(net, json_file.string());
270
271 std::ifstream file(json_file);
272 EXPECT_TRUE(file.good());
273
274 std::string content((std::istreambuf_iterator<char>(file)),
275 std::istreambuf_iterator<char>());
276
277 EXPECT_TRUE(content.find("\"num_nodes\"") != std::string::npos);
278 EXPECT_TRUE(content.find("\"arcs\"") != std::string::npos);
279 EXPECT_TRUE(content.find("\"cap\"") != std::string::npos);
280}
281
283{
284 auto net = generate_grid_network<TestNet>(2, 2, 10.0, false); // unidirectional
285
286 std::string json = network_to_json_string(net);
287
288 EXPECT_TRUE(json.find("\"num_nodes\": 4") != std::string::npos);
289 EXPECT_TRUE(json.find("\"source\"") != std::string::npos);
290 EXPECT_TRUE(json.find("\"sink\"") != std::string::npos);
291}
292
293
294//==============================================================================
295// DIMACS Export/Import Tests
296//==============================================================================
297
299{
300 auto net = generate_grid_network<TestNet>(3, 3, 10.0, false);
301
302 export_network_to_dimacs(net, dimacs_file.string());
303
304 std::ifstream file(dimacs_file);
305 EXPECT_TRUE(file.good());
306
307 std::string content((std::istreambuf_iterator<char>(file)),
308 std::istreambuf_iterator<char>());
309
310 EXPECT_TRUE(content.find("p max 9") != std::string::npos);
311 EXPECT_TRUE(content.find("n") != std::string::npos);
312 EXPECT_TRUE(content.find("a") != std::string::npos);
313}
314
316{
317 // Create and export
318 auto net1 = generate_grid_network<TestNet>(3, 3, 10.0, false);
319 export_network_to_dimacs(net1, dimacs_file.string());
320
321 // Import
322 auto net2 = import_network_from_dimacs<TestNet>(dimacs_file.string());
323
324 EXPECT_EQ(net2.vsize(), net1.vsize());
325 EXPECT_EQ(net2.esize(), net1.esize());
326 EXPECT_TRUE(net2.is_single_source());
327 EXPECT_TRUE(net2.is_single_sink());
328}
329
331{
332 // Create network, compute flow, export
333 auto net1 = generate_grid_network<TestNet>(4, 4, 10.0, false);
335
336 export_network_to_dimacs(net1, dimacs_file.string());
337
338 // Import and compute flow
339 auto net2 = import_network_from_dimacs<TestNet>(dimacs_file.string());
341
342 // Flows should be equal
344}
345
346
347//==============================================================================
348// Benchmark Utilities Tests
349//==============================================================================
350
352{
353 auto net = generate_grid_network<TestNet>(5, 5, 10.0, false); // unidirectional
354
355 auto result = benchmark_maxflow(net, [](auto& n) {
356 return dinic_maximum_flow(n);
357 }, "Dinic");
358
359 EXPECT_GT(result.flow_value, 0.0);
360 EXPECT_GE(result.elapsed_ms, 0.0);
361 EXPECT_EQ(result.algorithm_name, "Dinic");
362}
363
365{
366 std::vector<MaxFlowBenchmarkResult<double>> results;
367
368 results.push_back({100.0, 1.5, "Algorithm A"});
369 results.push_back({100.0, 2.3, "Algorithm B"});
370
371 // Just verify it doesn't crash
372 testing::internal::CaptureStdout();
374 std::string output = testing::internal::GetCapturedStdout();
375
376 EXPECT_TRUE(output.find("Algorithm A") != std::string::npos);
377 EXPECT_TRUE(output.find("Algorithm B") != std::string::npos);
378}
379
380
381//==============================================================================
382// Stress Tests
383//==============================================================================
384
386{
388 params.num_nodes = 100;
389 params.num_arcs = 500;
390 params.seed = 42;
391
393
394 EXPECT_EQ(net.vsize(), 100);
395 EXPECT_GE(net.esize(), 99); // At least connected path
396 // Don't run flow since multi-source/multi-sink
397}
398
400{
401 auto net = generate_grid_network<TestNet>(20, 20, 100.0, false); // unidirectional
402
403 EXPECT_EQ(net.vsize(), 400);
404
405 // Should complete in reasonable time
406 auto flow = dinic_maximum_flow(net);
407 EXPECT_GT(flow, 0.0);
408}
409
410
411int main(int argc, char** argv)
412{
413 ::testing::InitGoogleTest(&argc, argv);
414 return RUN_ALL_TESTS();
415}
int main()
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:798
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:462
Net::Flow_Type dinic_maximum_flow(Net &net)
Compute maximum flow using Dinic's algorithm.
void export_network_to_json(const Net &net, const std::string &filename)
Export network to JSON format.
Definition net_utils.H:672
std::string network_to_json_string(const Net &net)
Export network to JSON string.
Definition net_utils.H:728
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:935
void print_benchmark_results(const std::vector< MaxFlowBenchmarkResult< Flow_Type > > &results)
Print benchmark results.
Definition net_utils.H:954
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:557
DynList< T > maps(const C &c, Op op)
Classic map operation.
Network flow utilities: generators, visualization, serialization.
TEST_F(NetUtilsTest, GenerateRandomNetworkBasic)
static struct argp_option options[]
Definition ntreepic.C:1885
Filtered iterator on all the arcs of a graph.
Definition tpl_graph.H:1164
Options for DOT export.
Definition net_utils.H:427
bool show_flow
Show flow values on arcs.
Definition net_utils.H:428
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
Advanced maximum flow algorithms.
fstream file[12]
Definition treapObs.C:67
ofstream output
Definition writeHeap.C:213