Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
tpl_mincost_test.cc
Go to the documentation of this file.
1/* Tests for tpl_mincost.H - Advanced minimum cost flow algorithms
2 *
3 * Tests cover:
4 * - Successive Shortest Paths (SSP) algorithm
5 * - Cost Scaling algorithm
6 * - Assignment problem solver
7 * - Transportation problem solver
8 */
9
10#include <gtest/gtest.h>
11#include <tpl_mincost.H>
12#include <tpl_netcost.H>
13
14using namespace Aleph;
15
16namespace
17{
18 // Network type for tests
20 using Node = TestNet::Node;
21 using Arc = TestNet::Arc;
22
23 // Helper to compute total outgoing flow from source
24 double get_max_flow(const TestNet& net)
25 {
26 double flow = 0;
27 auto source = net.get_source();
28 for (Out_Iterator<TestNet> it(source); it.has_curr(); it.next_ne())
29 flow += it.get_curr()->flow;
30 return flow;
31 }
32
33 // Build a simple path network: s -> a -> t
35 {
36 TestNet net;
37 auto s = net.insert_node();
38 auto a = net.insert_node();
39 auto t = net.insert_node();
40
41 net.insert_arc(s, a, 10.0, 2.0); // cap=10, cost=2
42 net.insert_arc(a, t, 10.0, 3.0); // cap=10, cost=3
43
44 return net;
45 }
46
47 // Build diamond network with two paths
48 TestNet build_diamond_network()
49 {
50 TestNet net;
51 auto s = net.insert_node();
52 auto a = net.insert_node();
53 auto b = net.insert_node();
54 auto t = net.insert_node();
55
56 // Two paths: s->a->t (expensive) and s->b->t (cheap)
57 net.insert_arc(s, a, 5.0, 10.0); // cap=5, cost=10
58 net.insert_arc(s, b, 5.0, 1.0); // cap=5, cost=1
59 net.insert_arc(a, t, 5.0, 10.0); // cap=5, cost=10
60 net.insert_arc(b, t, 5.0, 1.0); // cap=5, cost=1
61
62 return net;
63 }
64
65 // Build network with cross-connection
67 {
68 TestNet net;
69 auto s = net.insert_node();
70 auto a = net.insert_node();
71 auto b = net.insert_node();
72 auto t = net.insert_node();
73
74 net.insert_arc(s, a, 10.0, 5.0); // s->a: cap=10, cost=5
75 net.insert_arc(s, b, 10.0, 2.0); // s->b: cap=10, cost=2
76 net.insert_arc(a, b, 5.0, 0.0); // a->b: cap=5, cost=0 (transfer)
77 net.insert_arc(a, t, 10.0, 3.0); // a->t: cap=10, cost=3
78 net.insert_arc(b, t, 10.0, 1.0); // b->t: cap=10, cost=1
79
80 return net;
81 }
82
83 // Build network with multiple parallel paths
85 {
86 TestNet net;
87 auto s = net.insert_node();
88 auto t = net.insert_node();
89
90 // Three parallel arcs with different costs
91 net.insert_arc(s, t, 5.0, 1.0); // cheap, limited
92 net.insert_arc(s, t, 3.0, 5.0); // medium
93 net.insert_arc(s, t, 2.0, 10.0); // expensive
94
95 return net;
96 }
97}
98
99
100//==============================================================================
101// Successive Shortest Paths (SSP) Tests
102//==============================================================================
103
105{
106 auto net = build_simple_path_network();
107
108 auto [flow, cost] = successive_shortest_paths(net);
109
110 // Max flow = 10, cost = 10*(2+3) = 50
111 EXPECT_DOUBLE_EQ(flow, 10.0);
112 EXPECT_DOUBLE_EQ(cost, 50.0);
114}
115
117{
118 auto net = build_diamond_network();
119
120 auto [flow, cost] = successive_shortest_paths(net);
121
122 // Max flow = 10 (5 via each path)
123 // SSP should prefer cheap path: s->b->t costs 1+1=2 per unit
124 // Then use expensive path: s->a->t costs 10+10=20 per unit
125 // Cost = 5*2 + 5*20 = 10 + 100 = 110
126 EXPECT_DOUBLE_EQ(flow, 10.0);
127 EXPECT_DOUBLE_EQ(cost, 110.0);
129}
130
132{
133 auto net = build_cross_network();
134
135 auto [flow, cost] = successive_shortest_paths(net);
136
137 // Should find path that uses cheap arcs
138 EXPECT_GT(flow, 0.0);
139 EXPECT_GT(cost, 0.0);
141}
142
144{
145 auto net = build_parallel_network();
146
147 auto [flow, cost] = successive_shortest_paths(net);
148
149 // Max flow = 5 + 3 + 2 = 10
150 // SSP uses cheap arc first (5 units @ cost 1 = 5)
151 // Then medium arc (3 units @ cost 5 = 15)
152 // Then expensive arc (2 units @ cost 10 = 20)
153 // Total cost = 5 + 15 + 20 = 40
154 EXPECT_DOUBLE_EQ(flow, 10.0);
155 EXPECT_DOUBLE_EQ(cost, 40.0);
157}
158
160{
161 TestNet net;
162 auto s = net.insert_node();
163 auto a = net.insert_node();
164 auto t = net.insert_node();
165
166 net.insert_arc(s, a, 5.0, 1000.0);
167 net.insert_arc(a, t, 5.0, 2000.0);
168
169 auto [flow, cost] = successive_shortest_paths(net);
170
171 EXPECT_DOUBLE_EQ(flow, 5.0);
172 EXPECT_DOUBLE_EQ(cost, 15000.0); // 5 * (1000 + 2000)
173}
174
176{
177 TestNet net;
178 auto s = net.insert_node();
179 auto t = net.insert_node();
180 net.insert_arc(s, t, 15.0, 3.0);
181
182 auto [flow, cost] = successive_shortest_paths(net);
183
184 EXPECT_DOUBLE_EQ(flow, 15.0);
185 EXPECT_DOUBLE_EQ(cost, 45.0); // 15 * 3
186}
187
189{
190 TestNet net;
191 auto s = net.insert_node();
192 auto t = net.insert_node();
193 net.insert_arc(s, t, 0.0, 5.0); // Zero capacity
194
195 auto [flow, cost] = successive_shortest_paths(net);
196
197 EXPECT_DOUBLE_EQ(flow, 0.0);
198 EXPECT_DOUBLE_EQ(cost, 0.0);
199}
200
202{
203 auto net = build_simple_path_network();
204
206 auto [flow, cost] = ssp(net);
207
208 EXPECT_DOUBLE_EQ(flow, 10.0);
209 EXPECT_DOUBLE_EQ(cost, 50.0);
210}
211
213{
214 // Build same network twice and compare results
215 auto net_ssp = build_diamond_network();
216 auto net_cc = build_diamond_network();
217
219 auto [cycles, factor] = max_flow_min_cost_by_cycle_canceling(net_cc);
220 (void)cycles;
221 (void)factor;
222
223 double flow_cc = get_max_flow(net_cc);
224 double cost_cc = net_cc.flow_cost();
225
226 // Both should find the same max flow
228
229 // Costs should be equal (both optimal)
231}
232
233
234//==============================================================================
235// Cost Scaling Tests
236//
237// NOTE: Cost Scaling algorithm is mentioned in the documentation but not yet
238// implemented in tpl_mincost.H. These tests are placeholders for when it gets
239// implemented. For now, we test SSP extensively and compare with Cycle Canceling.
240//==============================================================================
241
242// TODO: Uncomment these tests when cost_scaling_min_cost_flow is implemented
243/*
244TEST(CostScalingTest, SimplePath)
245{
246 auto net = build_simple_path_network();
247
248 auto [flow, cost] = cost_scaling_min_cost_flow(net);
249
250 EXPECT_DOUBLE_EQ(flow, 10.0);
251 EXPECT_DOUBLE_EQ(cost, 50.0);
252 EXPECT_TRUE(net.check_network());
253}
254
255TEST(CostScalingTest, DiamondNetwork)
256{
257 auto net = build_diamond_network();
258
259 auto [flow, cost] = cost_scaling_min_cost_flow(net);
260
261 EXPECT_DOUBLE_EQ(flow, 10.0);
262 EXPECT_DOUBLE_EQ(cost, 110.0);
263 EXPECT_TRUE(net.check_network());
264}
265*/
266
267
268//==============================================================================
269// Algorithm Comparison Tests
270//==============================================================================
271
273{
274 auto net1 = build_diamond_network();
275 auto net2 = build_diamond_network();
276
279
280 double flow_cc = get_max_flow(net2);
281 double cost_cc = net2.flow_cost();
282
283 // Both should find same max flow
285
286 // Both should find same min cost
288}
289
304
305
306//==============================================================================
307// Performance Comparison Tests
308//==============================================================================
309
311{
312 // Build a grid-like network
313 const int SIZE = 5;
314
315 auto build_grid = []() {
316 TestNet net;
317 std::vector<std::vector<Node*>> nodes(SIZE, std::vector<Node*>(SIZE));
318
319 for (int i = 0; i < SIZE; ++i)
320 for (int j = 0; j < SIZE; ++j)
321 nodes[i][j] = net.insert_node();
322
323 // Add horizontal arcs
324 for (int i = 0; i < SIZE; ++i)
325 for (int j = 0; j < SIZE - 1; ++j)
326 net.insert_arc(nodes[i][j], nodes[i][j+1],
327 10.0 + (i+j) % 5, // capacity
328 1.0 + (i*j) % 3); // cost
329
330 // Add vertical arcs
331 for (int i = 0; i < SIZE - 1; ++i)
332 for (int j = 0; j < SIZE; ++j)
333 net.insert_arc(nodes[i][j], nodes[i+1][j],
334 10.0 + (i+j+1) % 5,
335 1.0 + ((i+1)*j) % 3);
336
337 return net;
338 };
339
340 auto net_ssp = build_grid();
341 auto net_cc = build_grid();
342
345
346 double flow_cc = get_max_flow(net_cc);
347 double cost_cc = net_cc.flow_cost();
348
349 // Both algorithms should agree
352
353 std::cout << "\nGrid " << SIZE << "x" << SIZE << " results:\n";
354 std::cout << " SSP: Flow=" << flow_ssp << ", Cost=" << cost_ssp << "\n";
355 std::cout << " CC: Flow=" << flow_cc << ", Cost=" << cost_cc << "\n";
356}
357
358
359//==============================================================================
360// Assignment Problem Tests
361//==============================================================================
362
364{
365 std::vector<std::vector<double>> costs = {
366 {10, 5, 13},
367 {3, 9, 18},
368 {10, 6, 12}
369 };
370
371 auto result = solve_assignment(costs);
372
373 EXPECT_TRUE(result.feasible);
374 EXPECT_EQ(result.assignments.size(), 3);
375 EXPECT_DOUBLE_EQ(result.total_cost, 20.0);
376}
377
379{
380 std::vector<std::vector<double>> costs = {{42}};
381
382 auto result = solve_assignment(costs);
383
384 EXPECT_TRUE(result.feasible);
385 EXPECT_EQ(result.assignments.size(), 1);
386 EXPECT_DOUBLE_EQ(result.total_cost, 42.0);
387}
388
390{
391 std::vector<std::vector<double>> costs;
392
393 auto result = solve_assignment(costs);
394
395 EXPECT_TRUE(result.feasible);
396 EXPECT_EQ(result.assignments.size(), 0);
397}
398
400{
401 std::vector<std::vector<double>> costs = {
402 {9, 2, 7, 8, 3},
403 {6, 4, 3, 7, 5},
404 {5, 8, 1, 8, 6},
405 {7, 6, 9, 4, 5},
406 {3, 7, 2, 8, 2}
407 };
408
409 auto result = solve_assignment(costs);
410
411 EXPECT_TRUE(result.feasible);
412 EXPECT_EQ(result.assignments.size(), 5);
413 EXPECT_GT(result.total_cost, 0.0);
414}
415
416
417//==============================================================================
418// Transportation Problem Tests
419//==============================================================================
420
422{
423 std::vector<double> supplies = {100, 100};
424 std::vector<double> demands = {100, 100};
425 std::vector<std::vector<double>> costs = {
426 {1, 2},
427 {3, 4}
428 };
429
431
432 EXPECT_TRUE(result.feasible);
433 EXPECT_EQ(result.shipments.size(), 2);
434}
435
437{
438 std::vector<double> supplies = {100, 100};
439 std::vector<double> demands = {50, 50};
440
441 std::vector<std::vector<double>> costs = {
442 {1, 2},
443 {3, 4}
444 };
445
447
448 EXPECT_FALSE(result.feasible);
449}
450
452{
453 // 3 suppliers, 4 customers
454 std::vector<double> supplies = {50, 60, 40};
455 std::vector<double> demands = {30, 40, 50, 30};
456 std::vector<std::vector<double>> costs = {
457 {2, 3, 1, 4},
458 {3, 2, 4, 1},
459 {1, 4, 2, 3}
460 };
461
463
464 EXPECT_TRUE(result.feasible);
465 EXPECT_EQ(result.shipments.size(), 3);
466
467 // Verify shipments sum correctly
468 double total_shipped = 0;
469 for (size_t i = 0; i < result.shipments.size(); ++i)
470 for (size_t j = 0; j < result.shipments[i].size(); ++j)
471 total_shipped += result.shipments[i][j];
472
473 EXPECT_DOUBLE_EQ(total_shipped, 150.0); // Total supply/demand
474}
475
476
477int main(int argc, char** argv)
478{
479 ::testing::InitGoogleTest(&argc, argv);
480 return RUN_ALL_TESTS();
481}
int main()
WeightedDigraph::Node Node
WeightedDigraph::Arc Arc
bool has_curr() const noexcept
Return true the iterator has an current arc.
Definition tpl_graph.H:1645
Filtered iterator for outcoming arcs of a node.
Definition tpl_graph.H:1750
#define TEST(name)
DynArray< Graph::Node * > nodes
Definition graphpic.C:406
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
std::tuple< size_t, double > max_flow_min_cost_by_cycle_canceling(Net &net, double it_factor=0.4, size_t step=10)
Compute maximum flow at minimum cost using cycle canceling.
TransportationResult< Cost_Type > solve_transportation(const std::vector< Cost_Type > &supplies, const std::vector< Cost_Type > &demands, const std::vector< std::vector< Cost_Type > > &costs)
Solve the transportation problem using min-cost flow.
std::pair< typename Net::Flow_Type, typename Net::Flow_Type > successive_shortest_paths(Net &net)
Compute minimum cost maximum flow using Successive Shortest Paths.
AssignmentResult< Cost_Type > solve_assignment(const std::vector< std::vector< Cost_Type > > &costs)
Solve the assignment problem using min-cost flow.
DynList< T > maps(const C &c, Op op)
Classic map operation.
Capacitated flow network with costs associated to arcs.
Flow network implemented with adjacency lists.
Definition tpl_net.H:261
Node * insert_node(const Node_Type &node_info)
Insert a new node by copying node_info.
Definition tpl_net.H:559
bool check_network() const
Validate flow-conservation and capacity constraints.
Definition tpl_net.H:804
Node * get_source() const
Return an arbitrary source node.
Definition tpl_net.H:548
Arc * insert_arc(Node *src_node, Node *tgt_node, const Flow_Type &cap, const Flow_Type &flow, const typename Arc::Arc_Type &arc_info=Arc_Type())
Insert a capacitated arc with an initial flow.
Definition tpl_net.H:607
ArcT Arc
Arc type.
Definition tpl_net.H:272
NodeT Node
Node type.
Definition tpl_net.H:275
Functor wrapper for SSP algorithm.
Advanced minimum cost flow algorithms.
Maximum flow minimum cost network algorithms.