//================================================================================================== // G r a p h _ p r o b l e m Interface // M i n _ c o s t _ f l o w // C o s t _ s c a l i n g // By Bruno Bachelet //================================================================================================== // Copyright (c) 1999-2016 // Bruno Bachelet - bruno@nawouak.net - http://www.nawouak.net // // This file is part of the B++ Library. This library is free software; you can redistribute it // and/or modify it under the terms of the GNU Library General Public License as published by the // Free Software Foundation; either version 2 of the License, or (at your option) any later // version. // // This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; // without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See // the GNU Library General Public License for more details (http://www.gnu.org).
/*DESCRIPTION*/ /* This module implements the cost-scaling algorithm to solve the minimum cost flow problem (with piecewise linear and convex costs) in graphs. */
// File Name //------------------------------------------------------------------------------------- #line __LINE__ "graph_problem/min_cost_flow/cost_scaling.hpp"
// Guardian //-------------------------------------------------------------------------------------- #ifndef guGraphProblemMinCostFlowCostScaling #define guGraphProblemMinCostFlowCostScaling
// Headers //--------------------------------------------------------------------------------------- #include <list> /*INCLUDE*/ #include <bpp/graph_problem/min_cost_flow/algorithm.hpp> /*INCLUDE*/
namespace bpp {
// Importation/Exportation //----------------------------------------------------------------------- #ifdef GRAPH_PROBLEM_MIN_COST_FLOW_DLL #define dll_export DLL_EXPORT #else #define dll_export DLL_IMPORT #endif
// Namespaces //------------------------------------------------------------------------------------ #define public_area graphProblemMinCostFlowCostScaling #define private_area graphProblemMinCostFlowCostScaling_private
namespace public_area { /*NAMESPACE*/ using namespace graphProblemMinCostFlowAlgorithm; } namespace private_area { using namespace public_area; }
extern_module_name;
// Initialization //--------------------------------------------------------------------------------
// Macrocommands //--------------------------------------------------------------------------------- /*ALIAS*/ #define tdGraph class prArcData,class prNodeData //
/*ALIAS*/ #define tuGraph prArcData,prNodeData //
// Types & Classes //------------------------------------------------------------------------------- namespace public_area { template <tdGraph> class clLinearConformity; template <tdGraph> class clSolveAlgo; }
namespace private_area { template <tdGraph,class prConformity> class clCostScalingAdmissibility; }
// Functions Interface //--------------------------------------------------------------------------- namespace public_area { template <tdGraph,class prConformity> tyInteger findMinimumCostFlow(clGraph<tuGraph> &,const prConformity &); }
namespace private_area { template <tdGraph,class prConformity> tyInteger balanceNode(clNode<tuGraph> &,tyReal,std_list(clNode<tuGraph> *) &, const prConformity &,tyBoolean);
template <tdGraph,class prConformity> tyInteger findEpsilonOptimalFlow(clGraph<tuGraph> &,tyReal,const prConformity &,tyBoolean);
testing_mode ( function void test(void); ) }
// Errors //---------------------------------------------------------------------------------------- namespace public_area {}
// Constants & Variables //------------------------------------------------------------------------- namespace public_area {} namespace private_area {}
// L i n e a r C o n f o r m i t y Interface //---------------------------------------------------- namespace public_area { /*CLASS clLinearConformity */ /* Represents the conformity curve of an arc (the optimality conditions) for the minimum cost flow problem with linear costs. */ template <tdGraph> class clLinearConformity { //-------------------------------------------------------------------------------------------Types /*TYPE clLinearConformity */ /* Type of the arcs that carry the cost defined by the curve. */ public_property typedef clArc<tuGraph> cpArc; //-----------------------------------------------------------------------------------------Private private_property constructor clLinearConformity(const clLinearConformity &); private_property clLinearConformity & operator = (const clLinearConformity &); //------------------------------------------------------------------------------------------Public public_property constructor clLinearConformity(void); public_property destructor clLinearConformity(void);
public_property tyReal leftBoundary(const cpArc &) const; public_property tyReal rightBoundary(const cpArc &) const; public_property tyReal leftDerivative(const cpArc &) const; public_property tyReal rightDerivative(const cpArc &) const; public_property tyReal maximumDerivative(const cpArc &) const; public_property void nullCost(cpArc &) const; }; }
// S o l v e A l g o Interface //------------------------------------------------------------------ namespace public_area { /*CLASS clSolveAlgo */ /* Represents an algorithm to solve the minimum cost flow problem (with linear costs) in a graph using the cost-scaling method. */ template <tdGraph> class clSolveAlgo : public clSolver<tuGraph> { //-----------------------------------------------------------------------------------------Private private_property constructor clSolveAlgo(const clSolveAlgo &); private_property clSolveAlgo & operator = (const clSolveAlgo &); //------------------------------------------------------------------------------------------Public public_property constructor clSolveAlgo(void); public_property virtual destructor clSolveAlgo(void);
public_property tyInteger run(clGraph<tuGraph> &) const; }; }
// C o s t S c a l i n g A d m i s s i b i l i t y Interface //------------------------------------ namespace private_area { template <tdGraph,class prConformity> class clCostScalingAdmissibility { //-------------------------------------------------------------------------------------------Types public_property typedef clArc<tuGraph> cpArc; //-----------------------------------------------------------------------------------------Private private_property constructor clCostScalingAdmissibility(const clCostScalingAdmissibility &); private_property clCostScalingAdmissibility & operator = (const clCostScalingAdmissibility &);
private_property const prConformity & atConformity; private_property tyReal atEpsilon; //------------------------------------------------------------------------------------------Public public_property constructor clCostScalingAdmissibility(const prConformity &,tyReal); public_property destructor clCostScalingAdmissibility(void) {}
public_property tyBoolean direct(const cpArc &) const; public_property tyBoolean indirect(const cpArc &) const; }; }
// Functions Inline //------------------------------------------------------------------------------ namespace public_area {} namespace private_area {}
// L i n e a r C o n f o r m i t y Inline //------------------------------------------------------- namespace public_area { //--------------------------------------------------------------------------------------Constructor /*METHOD clLinearConformity */ /* Builds a conformity curve. */ template <tdGraph> inline clLinearConformity<tuGraph>::clLinearConformity(void) {} //---------------------------------------------------------------------------------------Destructor /*METHOD clLinearConformity */ /* Destructs the conformity curve. */ template <tdGraph> inline clLinearConformity<tuGraph>::~clLinearConformity(void) {} //-------------------------------------------------------------------------------------LeftBoundary /*METHOD clLinearConformity */ /* Returns the decrease boundary of the flow of an arc so the arc becomes or stays conform. */ template <tdGraph> inline tyReal clLinearConformity<tuGraph>::leftBoundary(const cpArc & agArc) const { return (agArc.data().minimum()); } //------------------------------------------------------------------------------------RightBoundary /*METHOD clLinearConformity */ /* Returns the increase boundary of the flow of an arc so the arc becomes or stays conform. */ template <tdGraph> inline tyReal clLinearConformity<tuGraph>::rightBoundary(const cpArc & agArc) const { return (agArc.data().maximum()); } //-----------------------------------------------------------------------------------LeftDerivative /*METHOD clLinearConformity */ /* Returns the decrease boundary of the tension of an arc so the arc becomes or stays conform. */ template <tdGraph> inline tyReal clLinearConformity<tuGraph>::leftDerivative(const cpArc & agArc) const { return (agArc.data().unitCost()); } //----------------------------------------------------------------------------------RightDerivative /*METHOD clLinearConformity */ /* Returns the increase boundary of the tension of an arc so the arc becomes or stays conform. */ template <tdGraph> inline tyReal clLinearConformity<tuGraph>::rightDerivative(const cpArc & agArc) const { return (agArc.data().unitCost()); } //--------------------------------------------------------------------------------MaximumDerivative /*METHOD clLinearConformity */ /* Returns the maximum possible increase boundary of the tension of an arc. */ template <tdGraph> inline tyReal clLinearConformity<tuGraph>::maximumDerivative(const cpArc & agArc) const { return (agArc.data().unitCost()); } //-----------------------------------------------------------------------------------------NullCost /*METHOD clLinearConformity */ /* Builds a null cost function for an arc. */ template <tdGraph> inline void clLinearConformity<tuGraph>::nullCost(cpArc & agArc) const { agArc.data().unitCost()=0; } }
// S o l v e A l g o Inline //--------------------------------------------------------------------- namespace public_area { //--------------------------------------------------------------------------------------Constructor /*METHOD clSolveAlgo */ /* Builds an algorithm to solve the minimum cost flow problem in a graph. */ template <tdGraph> inline clSolveAlgo<tuGraph>::clSolveAlgo(void) {} //---------------------------------------------------------------------------------------Destructor /*METHOD clSolveAlgo */ /* Destructs the algorithm. */ template <tdGraph> inline clSolveAlgo<tuGraph>::~clSolveAlgo(void) {} //----------------------------------------------------------------------------------------------Run /*METHOD clSolveAlgo */ /* Solves the minimum cost flow problem using the cost-scaling method. */ template <tdGraph> inline tyInteger clSolveAlgo<tuGraph>::run(clGraph<tuGraph> & agGraph) const { clLinearConformity<tuGraph> lcConformity;
return (findMinimumCostFlow(agGraph,lcConformity)); } }
// C o s t S c a l i n g A d m i s s i b i l i t y Inline //--------------------------------------- namespace private_area { //--------------------------------------------------------------------------------------Constructor template <tdGraph,class prConformity> inline clCostScalingAdmissibility<tuGraph,prConformity>:: clCostScalingAdmissibility(const prConformity & agConformity,tyReal agEpsilon) : atConformity(agConformity),atEpsilon(agEpsilon) {} //-------------------------------------------------------------------------------------------Direct template <tdGraph,class prConformity> inline tyBoolean clCostScalingAdmissibility<tuGraph,prConformity>::direct(const cpArc & agArc) const { tyReal lcIncrement;
tyReal lcTension = agArc.targetNode()->data().potential()-agArc.sourceNode()->data().potential(); tyReal lcDerivative = atConformity.rightDerivative(agArc);
if (lcTension>lcDerivative and lcTension<=lcDerivative+atEpsilon) { lcIncrement=atConformity.rightBoundary(agArc)-agArc.data().flow(); return (lcIncrement>0.0); }
return (false); } //-----------------------------------------------------------------------------------------Indirect template <tdGraph,class prConformity> inline tyBoolean clCostScalingAdmissibility<tuGraph,prConformity>::indirect(const cpArc & agArc) const { tyReal lcIncrement;
tyReal lcTension = agArc.targetNode()->data().potential()-agArc.sourceNode()->data().potential(); tyReal lcDerivative = atConformity.leftDerivative(agArc);
if (lcTension<lcDerivative and lcTension>=lcDerivative-atEpsilon) { lcIncrement=agArc.data().flow()-atConformity.leftBoundary(agArc); return (lcIncrement>0.0); }
return (false); } }
// Functions Implementation //---------------------------------------------------------------------- namespace public_area { //------------------------------------------------------------------------------FindMinimumCostFlow /*FUNCTION*/ /* Finds a minimum cost flow in a graph with any piecewise linear and convex costs. The second argument of the function is the description of the conformity curve (e.g. the class <CODE>clLinearConformity</CODE>). */ template <tdGraph,class prConformity> tyInteger findMinimumCostFlow(clGraph<tuGraph> & agGraph,const prConformity & agConformity) { method_name("findMinimumCostFlow");
typedef clArc<tuGraph> cpArc; typedef clNode<tuGraph> cpNode;
typedef typename clGraph<tuGraph>::cpArcX::const_iterator cpArcIterator1; typedef typename clNode<tuGraph>::cpArcX::const_iterator cpArcIterator2; typedef typename clGraph<tuGraph>::cpNodeX::const_iterator cpNodeIterator;
cpArcIterator1 lcCurrentArc1; cpArcIterator2 lcCurrentArc2; cpNodeIterator lcCurrentNode; tyReal lcDerivative; cpArcIterator1 lcLastArc1; cpArcIterator2 lcLastArc2; cpNodeIterator lcLastNode; cpArc * lcReturnLoop;
tyCardinal lcCounter = 0; tyReal lcEpsilon = -1.0; tyInteger lcNbIteration = 0; tyReal * lcRealS = new_array(tyReal,agGraph.nodes().size()); cpNode * lcSourceNode = firstNode(agGraph); cpNode * lcTargetNode = lastNode(agGraph);
// Flow Initialization // findCompatibleFlow(agGraph,false); if (agGraph.solved()==false) return (-1); agGraph.solved()=false;
// Return Loop Adding // if (lcSourceNode==nil or lcTargetNode==nil) send_error(erInvalidFlowGraph);
lcReturnLoop=new_object(cpArc(agGraph,agGraph.getNewArcKey(),prArcData(), lcTargetNode->key(),lcSourceNode->key()));
lcReturnLoop->data().minimum()=0.0; lcReturnLoop->data().maximum()=0.0; lcCurrentArc2=lcSourceNode->outgoingArcs().begin(); lcLastArc2=lcSourceNode->outgoingArcs().end();
while (lcCurrentArc2!=lcLastArc2) { lcReturnLoop->data().minimum()+=(*lcCurrentArc2).second->data().minimum(); lcReturnLoop->data().maximum()+=(*lcCurrentArc2).second->data().maximum(); lcCurrentArc2++; }
agConformity.nullCost(*lcReturnLoop); lcReturnLoop->data().flow()=0.0;
lcCurrentArc2=lcReturnLoop->sourceNode()->incomingArcs().begin(); lcLastArc2=lcReturnLoop->sourceNode()->incomingArcs().end();
while (lcCurrentArc2!=lcLastArc2) { lcReturnLoop->data().flow()+=(*lcCurrentArc2).second->data().flow(); lcCurrentArc2++; }
// Potential & Workspace Initialization // lcCurrentNode=agGraph.nodes().begin(); lcLastNode=agGraph.nodes().end();
while (lcCurrentNode!=lcLastNode) { (*lcCurrentNode).second->data().potential()=0.0; (*lcCurrentNode).second->work()=lcRealS+lcCounter; lcCurrentNode++; lcCounter++; }
// Maximal Cost Search // lcCurrentArc1=agGraph.arcs().begin(); lcLastArc1=agGraph.arcs().end();
while (lcCurrentArc1!=lcLastArc1) { lcDerivative=agConformity.maximumDerivative(*((*lcCurrentArc1).second)); if (lcDerivative>lcEpsilon) lcEpsilon=lcDerivative; lcCurrentArc1++; }
// Flow Improving // while (lcEpsilon>=1.0/agGraph.nodes().size()) { lcEpsilon/=2.0; lcNbIteration+=private_area::findEpsilonOptimalFlow(agGraph,lcEpsilon,agConformity,false); }
delete_array(lcRealS); delete_object(lcReturnLoop); agGraph.solved()=true; return (lcNbIteration); } }
namespace private_area { //--------------------------------------------------------------------------------------BalanceNode template <tdGraph,class prConformity> tyInteger balanceNode(clNode<tuGraph> & agNode,tyReal agEpsilon, std_list(clNode<tuGraph> *) & agNodeS,const prConformity & agConformity, tyBoolean agManageNodes) { typedef typename clNode<tuGraph>::cpArcX::const_iterator cpIterator;
clArc<tuGraph> * lcArc; tyBoolean lcBalanced; cpIterator lcCurrentArc; tyReal lcDerivative; tyReal lcIncrement; cpIterator lcLastArc; tyReal lcTension; tyReal * lcWork;
tyReal lcBalance = *((tyReal *)agNode.work()); tyInteger lcNbIteration = 0;
// Outgoing Arcs // lcCurrentArc=agNode.outgoingArcs().begin(); lcLastArc=agNode.outgoingArcs().end();
while (lcCurrentArc!=lcLastArc and lcBalance>0.0) { lcArc=(*lcCurrentArc).second; lcTension=lcArc->targetNode()->data().potential()-agNode.data().potential(); lcDerivative=agConformity.rightDerivative(*lcArc);
if (lcTension>lcDerivative and lcTension<=lcDerivative+agEpsilon) { lcIncrement=agConformity.rightBoundary(*lcArc)-lcArc->data().flow();
if (lcIncrement>0.0) { lcIncrement=mini(lcBalance,lcIncrement); lcArc->data().flow()+=lcIncrement; lcBalance-=lcIncrement;
lcWork=(tyReal *)(lcArc->targetNode()->work()); lcBalanced=(*lcWork <= 0.0); *lcWork+=lcIncrement; if (agManageNodes and lcBalanced and *lcWork>0.0) agNodeS.push_back(lcArc->targetNode()); ++lcNbIteration; } }
++lcCurrentArc; }
// Incoming Arcs // lcCurrentArc=agNode.incomingArcs().begin(); lcLastArc=agNode.incomingArcs().end();
while (lcCurrentArc!=lcLastArc and lcBalance>0.0) { lcArc=(*lcCurrentArc).second; lcTension=agNode.data().potential()-lcArc->sourceNode()->data().potential(); lcDerivative=agConformity.leftDerivative(*lcArc);
if (lcTension<lcDerivative and lcTension>=lcDerivative-agEpsilon) { lcIncrement=lcArc->data().flow()-agConformity.leftBoundary(*lcArc);
if (lcIncrement>0.0) { lcIncrement=mini(lcBalance,lcIncrement); lcArc->data().flow()-=lcIncrement; lcBalance-=lcIncrement;
lcWork=(tyReal *)(lcArc->sourceNode()->work()); lcBalanced=(*lcWork <= 0.0); *lcWork+=lcIncrement; if (agManageNodes and lcBalanced and *lcWork>0.0) agNodeS.push_back(lcArc->sourceNode()); ++lcNbIteration; } }
++lcCurrentArc; }
if (lcBalance>0.0) agNode.data().potential()-=agEpsilon; *((tyReal *)agNode.work())=lcBalance; return (lcNbIteration); } //---------------------------------------------------------------------------FindEpsilonOptimalFlow template <tdGraph,class prConformity> tyInteger findEpsilonOptimalFlow(clGraph<tuGraph> & agGraph,tyReal agEpsilon, const prConformity & agConformity, tyBoolean agWithTopologicalOrdering) { typedef std_list(clNode<tuGraph> *) cpNodeS; typedef std_vector(clNode<tuGraph> *) cpOrdering;
typedef typename clGraph<tuGraph>::cpArcX::const_iterator cpArcIterator1; typedef typename clNode<tuGraph>::cpArcX::const_iterator cpArcIterator2; typedef typename clGraph<tuGraph>::cpNodeX::const_iterator cpNodeIterator1; typedef typename cpNodeS::iterator cpNodeIterator2;
clArc<tuGraph> * lcArc; tyReal lcBalance; cpArcIterator2 lcCurrentArc2; cpNodeIterator2 lcCurrentNode2; cpArcIterator2 lcLastArc2; cpNodeIterator2 lcLastNode2; clNode<tuGraph> * lcNode; cpNodeS lcNodeS; cpOrdering lcOrdering; tyReal lcTension;
tyCardinal lcCounter = 0; cpArcIterator1 lcCurrentArc1 = agGraph.arcs().begin(); cpNodeIterator1 lcCurrentNode1 = agGraph.nodes().begin(); cpArcIterator1 lcLastArc1 = agGraph.arcs().end(); cpNodeIterator1 lcLastNode1 = agGraph.nodes().end(); tyInteger lcNbIteration = 0;
clCostScalingAdmissibility<tuGraph,prConformity> lcArcAdmissibility(agConformity,agEpsilon);
// Pseudo-Flow Building // while (lcCurrentArc1!=lcLastArc1) { lcArc=(*lcCurrentArc1).second; lcTension=lcArc->targetNode()->data().potential()-lcArc->sourceNode()->data().potential();
if (lcTension < agConformity.leftDerivative(*lcArc)-agEpsilon) lcArc->data().flow()=agConformity.leftBoundary(*lcArc); else if (lcTension > agConformity.rightDerivative(*lcArc)+agEpsilon) lcArc->data().flow()=agConformity.rightBoundary(*lcArc);
++lcCurrentArc1; }
// Node Balance Initialization // while (lcCurrentNode1!=lcLastNode1) { lcNode=(*lcCurrentNode1).second; lcBalance=0.0; lcCurrentArc2=lcNode->incomingArcs().begin(); lcLastArc2=lcNode->incomingArcs().end();
while (lcCurrentArc2!=lcLastArc2) { lcBalance+=(*lcCurrentArc2).second->data().flow(); ++lcCurrentArc2; }
lcCurrentArc2=lcNode->outgoingArcs().begin(); lcLastArc2=lcNode->outgoingArcs().end();
while (lcCurrentArc2!=lcLastArc2) { lcBalance-=(*lcCurrentArc2).second->data().flow(); ++lcCurrentArc2; }
*((tyReal *)(lcNode->work()))=lcBalance; if (not agWithTopologicalOrdering and lcBalance>0.0) lcNodeS.push_back(lcNode); ++lcCurrentNode1; }
// Flow Building // if (agWithTopologicalOrdering) { findTopologicalOrdering(agGraph,lcArcAdmissibility,lcOrdering);
while (lcCounter<lcOrdering.size()) { lcNodeS.push_back(lcOrdering[lcCounter]); ++lcCounter; } }
lcCurrentNode2=lcNodeS.begin(); lcLastNode2=lcNodeS.end();
while (lcCurrentNode2!=lcLastNode2) { lcNode=(*lcCurrentNode2);
if (agWithTopologicalOrdering) { if (*((tyReal *)lcNode->work())>0.0) { lcNbIteration+=balanceNode(*lcNode,agEpsilon,lcNodeS,agConformity,false);
if (*((tyReal *)lcNode->work())>0.0) { lcNodeS.erase(lcCurrentNode2); lcNodeS.push_front(lcNode); lcCurrentNode2=lcNodeS.begin(); lcLastNode2=lcNodeS.end(); } else ++lcCurrentNode2; } else ++lcCurrentNode2; } else { do { lcNbIteration+=balanceNode(*lcNode,agEpsilon,lcNodeS,agConformity,true); } while (*((tyReal *)lcNode->work())>0.0);
++lcCurrentNode2; } }
return (lcNbIteration); } }
// End //------------------------------------------------------------------------------------------- } #undef dll_export #undef tdGraph #undef tuGraph #undef public_area #undef private_area #endif |
//================================================================================================== // G r a p h _ p r o b l e m Implementation // M i n _ c o s t _ f l o w // C o s t _ s c a l i n g // By Bruno Bachelet //================================================================================================== // Copyright (c) 1999-2016 // Bruno Bachelet - bruno@nawouak.net - http://www.nawouak.net // // This file is part of the B++ Library. This library is free software; you can redistribute it // and/or modify it under the terms of the GNU Library General Public License as published by the // Free Software Foundation; either version 2 of the License, or (at your option) any later // version. // // This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; // without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See // the GNU Library General Public License for more details (http://www.gnu.org).
// File Name //------------------------------------------------------------------------------------- #line __LINE__ "graph_problem/min_cost_flow/cost_scaling.cpp"
// DLL Belonging //--------------------------------------------------------------------------------- #define GRAPH_PROBLEM_MIN_COST_FLOW_DLL
// Headers //--------------------------------------------------------------------------------------- #include <bpp/graph_problem/min_cost_flow/cost_scaling.hpp> /*INTERFACE*/
namespace bpp {
// Namespaces //------------------------------------------------------------------------------------ #define public_area graphProblemMinCostFlowCostScaling #define private_area graphProblemMinCostFlowCostScaling_private #define dll_export DLL_EXPORT
namespace public_area {} namespace private_area {}
static_module_name("Graph_problem/Min_cost_flow/Cost_scaling");
// Initialization //--------------------------------------------------------------------------------
// Errors //---------------------------------------------------------------------------------------- namespace public_area {}
// Constants & Variables //------------------------------------------------------------------------- namespace public_area {} namespace private_area {}
// Static Members //-------------------------------------------------------------------------------- namespace public_area {} namespace private_area {}
// Functions Implementation //---------------------------------------------------------------------- namespace public_area {} namespace private_area {}
// X X X Implementation //------------------------------------------------------------------------- namespace {}
// End //------------------------------------------------------------------------------------------- } |
|