/* -*- C++ -*- * * This file is a part of LEMON, a generic C++ optimization library * * Copyright (C) 2003-2008 * Egervary Jeno Kombinatorikus Optimalizalasi Kutatocsoport * (Egervary Research Group on Combinatorial Optimization, EGRES). * * Permission to use, modify and distribute this software is granted * provided that this copyright notice appears in all copies. For * precise terms see the accompanying LICENSE file. * * This software is provided "AS IS" with no warranty of any kind, * express or implied, and with no claim as to its suitability for any * purpose. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "test_tools.h" using namespace lemon; // Checks the feasibility of a flow template < typename Graph, typename LowerMap, typename CapacityMap, typename SupplyMap, typename FlowMap > bool checkFlow( const Graph& gr, const LowerMap& lower, const CapacityMap& upper, const SupplyMap& supply, const FlowMap& flow ) { GRAPH_TYPEDEFS(typename Graph); for (EdgeIt e(gr); e != INVALID; ++e) if (flow[e] < lower[e] || flow[e] > upper[e]) return false; for (NodeIt n(gr); n != INVALID; ++n) { typename SupplyMap::Value sum = 0; for (OutEdgeIt e(gr, n); e != INVALID; ++e) sum += flow[e]; for (InEdgeIt e(gr, n); e != INVALID; ++e) sum -= flow[e]; if (sum != supply[n]) return false; } return true; } // Checks the optimalitiy of a flow template < typename Graph, typename LowerMap, typename CapacityMap, typename CostMap, typename FlowMap, typename PotentialMap > bool checkOptimality( const Graph& gr, const LowerMap& lower, const CapacityMap& upper, const CostMap& cost, const FlowMap& flow, const PotentialMap& pi ) { GRAPH_TYPEDEFS(typename Graph); // Checking the Complementary Slackness optimality condition bool opt = true; for (EdgeIt e(gr); e != INVALID; ++e) { typename CostMap::Value red_cost = cost[e] + pi[gr.source(e)] - pi[gr.target(e)]; opt = red_cost == 0 || (red_cost > 0 && flow[e] == lower[e]) || (red_cost < 0 && flow[e] == upper[e]); if (!opt) break; } return opt; } // Runs a minimum cost flow algorithm and checks the results template < typename MinCostFlowImpl, typename Graph, typename LowerMap, typename CapacityMap, typename CostMap, typename SupplyMap > void checkMcf( std::string test_id, const MinCostFlowImpl& mcf, const Graph& gr, const LowerMap& lower, const CapacityMap& upper, const CostMap& cost, const SupplyMap& supply, bool mcf_result, bool result, typename CostMap::Value total ) { check(mcf_result == result, "Wrong result " + test_id); if (result) { check(checkFlow(gr, lower, upper, supply, mcf.flowMap()), "The flow is not feasible " + test_id); check(mcf.totalCost() == total, "The flow is not optimal " + test_id); check(checkOptimality(gr, lower, upper, cost, mcf.flowMap(), mcf.potentialMap()), "Wrong potentials " + test_id); } } int main() { // Various tests on a small graph { typedef ListGraph Graph; GRAPH_TYPEDEFS(ListGraph); // Reading the test graph Graph gr; Graph::EdgeMap c(gr), l1(gr), l2(gr), u(gr); Graph::NodeMap s1(gr), s2(gr), s3(gr); Node v, w; std::string fname; if(getenv("srcdir")) fname = std::string(getenv("srcdir")); else fname = "."; fname += "/test/min_cost_flow_test.lgf"; std::ifstream input(fname.c_str()); check(input, "Input file '" << fname << "' not found"); GraphReader(input, gr). readEdgeMap("cost", c). readEdgeMap("capacity", u). readEdgeMap("lower1", l1). readEdgeMap("lower2", l2). readNodeMap("supply1", s1). readNodeMap("supply2", s2). readNodeMap("supply3", s3). readNode("source", v). readNode("target", w). run(); input.close(); // Testing CapacityScaling (scaling enabled) { CapacityScaling mcf1(gr,u,c,s1); checkMcf("#A1",mcf1,gr,l1,u,c,s1,mcf1.run(),true, 0); CapacityScaling mcf2(gr,u,c,s2); checkMcf("#A2",mcf2,gr,l1,u,c,s2,mcf2.run(),true, 5240); CapacityScaling mcf3(gr,u,c,v,w,27); checkMcf("#A3",mcf3,gr,l1,u,c,s3,mcf3.run(),true, 7620); CapacityScaling mcf4(gr,l2,u,c,s1); checkMcf("#A4",mcf4,gr,l2,u,c,s1,mcf4.run(),false, 0); CapacityScaling mcf5(gr,l2,u,c,s2); checkMcf("#A5",mcf5,gr,l2,u,c,s2,mcf5.run(),true, 5970); CapacityScaling mcf6(gr,l2,u,c,v,w,27); checkMcf("#A6",mcf6,gr,l2,u,c,s3,mcf6.run(),true, 8010); } // Testing CapacityScaling (scaling disabled) { CapacityScaling mcf1(gr,u,c,s1); checkMcf("#B1",mcf1,gr,l1,u,c,s1,mcf1.run(false),true, 0); CapacityScaling mcf2(gr,u,c,s2); checkMcf("#B2",mcf2,gr,l1,u,c,s2,mcf2.run(false),true, 5240); CapacityScaling mcf3(gr,u,c,v,w,27); checkMcf("#B3",mcf3,gr,l1,u,c,s3,mcf3.run(false),true, 7620); CapacityScaling mcf4(gr,l2,u,c,s1); checkMcf("#B4",mcf4,gr,l2,u,c,s1,mcf4.run(false),false, 0); CapacityScaling mcf5(gr,l2,u,c,s2); checkMcf("#B5",mcf5,gr,l2,u,c,s2,mcf5.run(false),true, 5970); CapacityScaling mcf6(gr,l2,u,c,v,w,27); checkMcf("#B6",mcf6,gr,l2,u,c,s3,mcf6.run(false),true, 8010); } // Testing CostScaling { CostScaling mcf1(gr,u,c,s1); checkMcf("#C1",mcf1,gr,l1,u,c,s1,mcf1.run(),true, 0); CostScaling mcf2(gr,u,c,s2); checkMcf("#C2",mcf2,gr,l1,u,c,s2,mcf2.run(),true, 5240); CostScaling mcf3(gr,u,c,v,w,27); checkMcf("#C3",mcf3,gr,l1,u,c,s3,mcf3.run(),true, 7620); CostScaling mcf4(gr,l2,u,c,s1); checkMcf("#C4",mcf4,gr,l2,u,c,s1,mcf4.run(),false, 0); CostScaling mcf5(gr,l2,u,c,s2); checkMcf("#C5",mcf5,gr,l2,u,c,s2,mcf5.run(),true, 5970); CostScaling mcf6(gr,l2,u,c,v,w,27); checkMcf("#C6",mcf6,gr,l2,u,c,s3,mcf6.run(),true, 8010); } // Testing NetworkSimplex (with the default pivot rule) { NetworkSimplex mcf1(gr,u,c,s1); checkMcf("#D1",mcf1,gr,l1,u,c,s1,mcf1.run(),true, 0); NetworkSimplex mcf2(gr,u,c,s2); checkMcf("#D2",mcf2,gr,l1,u,c,s2,mcf2.run(),true, 5240); NetworkSimplex mcf3(gr,u,c,v,w,27); checkMcf("#D3",mcf3,gr,l1,u,c,s3,mcf3.run(),true, 7620); NetworkSimplex mcf4(gr,l2,u,c,s1); checkMcf("#D4",mcf4,gr,l2,u,c,s1,mcf4.run(),false, 0); NetworkSimplex mcf5(gr,l2,u,c,s2); checkMcf("#D5",mcf5,gr,l2,u,c,s2,mcf5.run(),true, 5970); NetworkSimplex mcf6(gr,l2,u,c,v,w,27); checkMcf("#D6",mcf6,gr,l2,u,c,s3,mcf6.run(),true, 8010); } // Testing NetworkSimplex (with FIRST_ELIGIBLE_PIVOT) { NetworkSimplex::PivotRuleEnum pr = NetworkSimplex::FIRST_ELIGIBLE_PIVOT; NetworkSimplex mcf1(gr,u,c,s1); checkMcf("#E1",mcf1,gr,l1,u,c,s1,mcf1.run(pr),true, 0); NetworkSimplex mcf2(gr,u,c,s2); checkMcf("#E2",mcf2,gr,l1,u,c,s2,mcf2.run(pr),true, 5240); NetworkSimplex mcf3(gr,u,c,v,w,27); checkMcf("#E3",mcf3,gr,l1,u,c,s3,mcf3.run(pr),true, 7620); NetworkSimplex mcf4(gr,l2,u,c,s1); checkMcf("#E4",mcf4,gr,l2,u,c,s1,mcf4.run(pr),false, 0); NetworkSimplex mcf5(gr,l2,u,c,s2); checkMcf("#E5",mcf5,gr,l2,u,c,s2,mcf5.run(pr),true, 5970); NetworkSimplex mcf6(gr,l2,u,c,v,w,27); checkMcf("#E6",mcf6,gr,l2,u,c,s3,mcf6.run(pr),true, 8010); } // Testing NetworkSimplex (with BEST_ELIGIBLE_PIVOT) { NetworkSimplex::PivotRuleEnum pr = NetworkSimplex::BEST_ELIGIBLE_PIVOT; NetworkSimplex mcf1(gr,u,c,s1); checkMcf("#F1",mcf1,gr,l1,u,c,s1,mcf1.run(pr),true, 0); NetworkSimplex mcf2(gr,u,c,s2); checkMcf("#F2",mcf2,gr,l1,u,c,s2,mcf2.run(pr),true, 5240); NetworkSimplex mcf3(gr,u,c,v,w,27); checkMcf("#F3",mcf3,gr,l1,u,c,s3,mcf3.run(pr),true, 7620); NetworkSimplex mcf4(gr,l2,u,c,s1); checkMcf("#F4",mcf4,gr,l2,u,c,s1,mcf4.run(pr),false, 0); NetworkSimplex mcf5(gr,l2,u,c,s2); checkMcf("#F5",mcf5,gr,l2,u,c,s2,mcf5.run(pr),true, 5970); NetworkSimplex mcf6(gr,l2,u,c,v,w,27); checkMcf("#F6",mcf6,gr,l2,u,c,s3,mcf6.run(pr),true, 8010); } // Testing NetworkSimplex (with BLOCK_SEARCH_PIVOT) { NetworkSimplex::PivotRuleEnum pr = NetworkSimplex::BLOCK_SEARCH_PIVOT; NetworkSimplex mcf1(gr,u,c,s1); checkMcf("#G1",mcf1,gr,l1,u,c,s1,mcf1.run(pr),true, 0); NetworkSimplex mcf2(gr,u,c,s2); checkMcf("#G2",mcf2,gr,l1,u,c,s2,mcf2.run(pr),true, 5240); NetworkSimplex mcf3(gr,u,c,v,w,27); checkMcf("#G3",mcf3,gr,l1,u,c,s3,mcf3.run(pr),true, 7620); NetworkSimplex mcf4(gr,l2,u,c,s1); checkMcf("#G4",mcf4,gr,l2,u,c,s1,mcf4.run(pr),false, 0); NetworkSimplex mcf5(gr,l2,u,c,s2); checkMcf("#G5",mcf5,gr,l2,u,c,s2,mcf5.run(pr),true, 5970); NetworkSimplex mcf6(gr,l2,u,c,v,w,27); checkMcf("#G6",mcf6,gr,l2,u,c,s3,mcf6.run(pr),true, 8010); } // Testing NetworkSimplex (with CANDIDATE_LIST_PIVOT) { NetworkSimplex::PivotRuleEnum pr = NetworkSimplex::CANDIDATE_LIST_PIVOT; NetworkSimplex mcf1(gr,u,c,s1); checkMcf("#I1",mcf1,gr,l1,u,c,s1,mcf1.run(pr),true, 0); NetworkSimplex mcf2(gr,u,c,s2); checkMcf("#I2",mcf2,gr,l1,u,c,s2,mcf2.run(pr),true, 5240); NetworkSimplex mcf3(gr,u,c,v,w,27); checkMcf("#I3",mcf3,gr,l1,u,c,s3,mcf3.run(pr),true, 7620); NetworkSimplex mcf4(gr,l2,u,c,s1); checkMcf("#I4",mcf4,gr,l2,u,c,s1,mcf4.run(pr),false, 0); NetworkSimplex mcf5(gr,l2,u,c,s2); checkMcf("#I5",mcf5,gr,l2,u,c,s2,mcf5.run(pr),true, 5970); NetworkSimplex mcf6(gr,l2,u,c,v,w,27); checkMcf("#I6",mcf6,gr,l2,u,c,s3,mcf6.run(pr),true, 8010); } // Testing NetworkSimplex (with ALTERING_LIST_PIVOT) { NetworkSimplex::PivotRuleEnum pr = NetworkSimplex::ALTERING_LIST_PIVOT; NetworkSimplex mcf1(gr,u,c,s1); checkMcf("#H1",mcf1,gr,l1,u,c,s1,mcf1.run(pr),true, 0); NetworkSimplex mcf2(gr,u,c,s2); checkMcf("#H2",mcf2,gr,l1,u,c,s2,mcf2.run(pr),true, 5240); NetworkSimplex mcf3(gr,u,c,v,w,27); checkMcf("#H3",mcf3,gr,l1,u,c,s3,mcf3.run(pr),true, 7620); NetworkSimplex mcf4(gr,l2,u,c,s1); checkMcf("#H4",mcf4,gr,l2,u,c,s1,mcf4.run(pr),false, 0); NetworkSimplex mcf5(gr,l2,u,c,s2); checkMcf("#H5",mcf5,gr,l2,u,c,s2,mcf5.run(pr),true, 5970); NetworkSimplex mcf6(gr,l2,u,c,v,w,27); checkMcf("#H6",mcf6,gr,l2,u,c,s3,mcf6.run(pr),true, 8010); } // Testing CycleCanceling (with BellmanFord) { CycleCanceling mcf1(gr,u,c,s1); checkMcf("#J1",mcf1,gr,l1,u,c,s1,mcf1.run(),true, 0); CycleCanceling mcf2(gr,u,c,s2); checkMcf("#J2",mcf2,gr,l1,u,c,s2,mcf2.run(),true, 5240); CycleCanceling mcf3(gr,u,c,v,w,27); checkMcf("#J3",mcf3,gr,l1,u,c,s3,mcf3.run(),true, 7620); CycleCanceling mcf4(gr,l2,u,c,s1); checkMcf("#J4",mcf4,gr,l2,u,c,s1,mcf4.run(),false, 0); CycleCanceling mcf5(gr,l2,u,c,s2); checkMcf("#J5",mcf5,gr,l2,u,c,s2,mcf5.run(),true, 5970); CycleCanceling mcf6(gr,l2,u,c,v,w,27); checkMcf("#J6",mcf6,gr,l2,u,c,s3,mcf6.run(),true, 8010); } // Testing CycleCanceling (with MinMeanCycle) { CycleCanceling mcf1(gr,u,c,s1); checkMcf("#K1",mcf1,gr,l1,u,c,s1,mcf1.run(true),true, 0); CycleCanceling mcf2(gr,u,c,s2); checkMcf("#K2",mcf2,gr,l1,u,c,s2,mcf2.run(true),true, 5240); CycleCanceling mcf3(gr,u,c,v,w,27); checkMcf("#K3",mcf3,gr,l1,u,c,s3,mcf3.run(true),true, 7620); CycleCanceling mcf4(gr,l2,u,c,s1); checkMcf("#K4",mcf4,gr,l2,u,c,s1,mcf4.run(true),false, 0); CycleCanceling mcf5(gr,l2,u,c,s2); checkMcf("#K5",mcf5,gr,l2,u,c,s2,mcf5.run(true),true, 5970); CycleCanceling mcf6(gr,l2,u,c,v,w,27); checkMcf("#K6",mcf6,gr,l2,u,c,s3,mcf6.run(true),true, 8010); } // Testing MinCostFlow { MinCostFlow mcf1(gr,u,c,s1); checkMcf("#L1",mcf1,gr,l1,u,c,s1,mcf1.run(),true, 0); MinCostFlow mcf2(gr,u,c,s2); checkMcf("#L2",mcf2,gr,l1,u,c,s2,mcf2.run(),true, 5240); MinCostFlow mcf3(gr,u,c,v,w,27); checkMcf("#L3",mcf3,gr,l1,u,c,s3,mcf3.run(),true, 7620); MinCostFlow mcf4(gr,l2,u,c,s1); checkMcf("#L4",mcf4,gr,l2,u,c,s1,mcf4.run(),false, 0); MinCostFlow mcf5(gr,l2,u,c,s2); checkMcf("#L5",mcf5,gr,l2,u,c,s2,mcf5.run(),true, 5970); MinCostFlow mcf6(gr,l2,u,c,v,w,27); checkMcf("#L6",mcf6,gr,l2,u,c,s3,mcf6.run(),true, 8010); } // Testing MinCostMaxFlow { MinCostMaxFlow mcmf(gr,u,c,v,w); mcmf.run(); checkMcf("#M1",mcmf,gr,l1,u,c,s3,true,true,7620); } } // Benchmark test on a DIMACS network { typedef SmartGraph Graph; GRAPH_TYPEDEFS(SmartGraph); // Reading the test graph Graph graph; Graph::EdgeMap lower(graph), capacity(graph), cost(graph); Graph::NodeMap supply(graph); std::string fname; if(getenv("srcdir")) fname = std::string(getenv("srcdir")); else fname = "."; fname += "/test/min_cost_flow_test.net"; std::ifstream input(fname.c_str()); check(input, "Input file '" << fname << "' not found"); readDimacs(input, graph, lower, capacity, cost, supply); input.close(); // NetworkSimplex { Timer t; NetworkSimplex mcf(graph, lower, capacity, cost, supply); bool res = mcf.run(); t.stop(); checkMcf("#T3", mcf, graph, lower, capacity, cost, supply, res, true, 196587626); std::cout << "NetworkSimplex"; std::cout << std::endl << t << std::endl; } // CapacityScaling { Timer t; CapacityScaling mcf(graph, lower, capacity, cost, supply); bool res = mcf.run(); t.stop(); checkMcf("#T1", mcf, graph, lower, capacity, cost, supply, res, true, 196587626); std::cout << "CapacityScaling"; std::cout << std::endl << t << std::endl; } // CostScaling { Timer t; CostScaling mcf(graph, lower, capacity, cost, supply); bool res = mcf.run(); t.stop(); checkMcf("#T2", mcf, graph, lower, capacity, cost, supply, res, true, 196587626); std::cout << "CostScaling"; std::cout << std::endl << t << std::endl; } // CycleCanceling { Timer t; CycleCanceling mcf(graph, lower, capacity, cost, supply); bool res = mcf.run(); t.stop(); checkMcf("#T4", mcf, graph, lower, capacity, cost, supply, res, true, 196587626); std::cout << "CycleCanceling"; std::cout << std::endl << t << std::endl; } } return 0; }