From b19bd8d8a41664328f33c9b459b2b0100f0b303f Mon Sep 17 00:00:00 2001 From: Carlo Zancanaro Date: Fri, 9 Nov 2012 12:17:46 +1100 Subject: Add an MCF operator to the separate solver For the solver utility it'd be good to have MCF problems, so here they are! Format is: MCF(cost*) Supplies is a [int,int,int,...], where each int represents a new node Arcs is [int:int, int:int, int:int, ...] where each int:int pair represents an edge from the first to the second (1 indexed from the "supplies" array). Costs is the argument to the function. There must be as many costs as arcs, and they are set from left to right, in order. --- impl/Complete.hpp | 10 + impl/EquationSystem.g | 18 +- impl/MCFClass.h | 2438 ++++++++++++++++++++++++++++ impl/MCFSimplex.cpp | 4198 +++++++++++++++++++++++++++++++++++++++++++++++++ impl/MCFSimplex.h | 1086 +++++++++++++ impl/MCFSimplex.o | Bin 0 -> 205412 bytes impl/Makefile | 17 +- impl/OPTUtils.h | 475 ++++++ impl/Operator.hpp | 82 + impl/main.cpp | 73 +- 10 files changed, 8383 insertions(+), 14 deletions(-) create mode 100755 impl/MCFClass.h create mode 100644 impl/MCFSimplex.cpp create mode 100644 impl/MCFSimplex.h create mode 100644 impl/MCFSimplex.o create mode 100755 impl/OPTUtils.h (limited to 'impl') diff --git a/impl/Complete.hpp b/impl/Complete.hpp index e3ec15a..664d71f 100644 --- a/impl/Complete.hpp +++ b/impl/Complete.hpp @@ -99,6 +99,16 @@ struct Complete { template friend std::ostream& operator<<(std::ostream&, const Complete&); + template + S as() const { + if (_infinity) { + if (_value > 0) + return infinity(); + return -infinity(); + } + return (S) _value; + } + private: T _value; bool _infinity; diff --git a/impl/EquationSystem.g b/impl/EquationSystem.g index 4884ca9..3ef9009 100644 --- a/impl/EquationSystem.g +++ b/impl/EquationSystem.g @@ -14,6 +14,7 @@ tokens { GUARD = 'guard' ; GREATER_EQUAL = '>=' ; QUESTION_MARK = '?' ; + MCF = 'MCF' ; MAXIMUM = 'max' ; MINIMUM = 'min' ; NEWLINE = '\n' ; @@ -26,18 +27,23 @@ maxExpr : MAXIMUM^ '('! minExpr ( ','! minExpr )* ')'! | minExpr ; minExpr : MINIMUM^ '('! maxExpr ( ','! maxExpr )* ')'! | expr ; expr : '(' expr GREATER_EQUAL expr QUESTION_MARK expr ')' -> ^(GUARD expr expr expr) - | GUARD^ '('! maxExpr ','! maxExpr ','! maxExpr ')'! - | 'add'^ '('! maxExpr ( ','! maxExpr )* ')'! - | 'sub'^ '('! maxExpr ( ','! maxExpr )* ')'! - | 'mult'^ '('! maxExpr ( ','! maxExpr )* ')'! - | term ( (PLUS | MULT | SUB | COMMA)^ expr )* ; + | GUARD^ '('! maxExpr ','! maxExpr ','! maxExpr ')'! + | MCF^ '<'! supplies ','! arcs '>'! '('! maxExpr (','! maxExpr)* ')'! + | 'add'^ '('! maxExpr ( ','! maxExpr )* ')'! + | 'sub'^ '('! maxExpr ( ','! maxExpr )* ')'! + | 'mult'^ '('! maxExpr ( ','! maxExpr )* ')'! + | term ( (PLUS | MULT | SUB | COMMA)^ expr )* ; + +supplies : '['^ NUMBER (','! NUMBER)* ']'! ; +arc : NUMBER ':'^ NUMBER ; +arcs : '['^ arc (','! arc)* ']'! ; term : NUMBER | VARIABLE | '-'^ term ; -NUMBER : (DIGIT)+ ; +NUMBER : '-'? (DIGIT)+ ; VARIABLE: (LETTER) (LETTER | DIGIT | '-' | '\[' | ']' )* ; WHITESPACE : ( '\t' | ' ' | '\u000C' )+ { diff --git a/impl/MCFClass.h b/impl/MCFClass.h new file mode 100755 index 0000000..f10cb5a --- /dev/null +++ b/impl/MCFClass.h @@ -0,0 +1,2438 @@ +/*--------------------------------------------------------------------------*/ +/*-------------------------- File MCFClass.h -------------------------------*/ +/*--------------------------------------------------------------------------*/ +/** @file + * Header file for the abstract base class MCFClass, which defines a standard + * interface for (linear or convex quadratic separable) Min Cost Flow Problem + * solvers, to be implemented as derived classes. + * + * \version 3.01 + * + * \date 30 - 09 - 2011 + * + * \author Alessandro Bertolini \n + * Operations Research Group \n + * Dipartimento di Informatica \n + * Universita' di Pisa \n + * + * \author Antonio Frangioni \n + * Operations Research Group \n + * Dipartimento di Informatica \n + * Universita' di Pisa \n + * + * \author Claudio Gentile \n + * Istituto di Analisi di Sistemi e Informatica \n + * Consiglio Nazionale delle Ricerche \n + * + * Copyright © 1996 - 2011 by Antonio Frangioni, Claudio Gentile + */ + +/*--------------------------------------------------------------------------*/ +/*--------------------------------------------------------------------------*/ +/*----------------------------- DEFINITIONS --------------------------------*/ +/*--------------------------------------------------------------------------*/ +/*--------------------------------------------------------------------------*/ + +#ifndef __MCFClass + #define __MCFClass /* self-identification: #endif at the end of the file */ + +/*--------------------------------------------------------------------------*/ +/*--------------------------------- MACROS ---------------------------------*/ +/*--------------------------------------------------------------------------*/ +/** @defgroup MCFCLASS_MACROS Compile-time switches in MCFClass.h + These macros control some important details of the class interface. + Although using macros for activating features of the interface is not + very C++, switching off some unused features may allow some + implementation to be more efficient in running time or memory. + @{ */ + +/*-------------------------------- USENAME0 --------------------------------*/ + +#define USENAME0 1 + +/**< Decides if 0 or 1 is the "name" of the first node. + If USENAME0 == 1, (warning: it has to be *exactly* 1), then the node + names go from 0 to n - 1, otherwise from 1 to n. Note that this does not + affect the position of the deficit in the deficit vectors, i.e., the + deficit of the i-th node - be its "name" `i' or `i - 1' - is always in + the i-th position of the vector. */ + +/*@} end( group( MCFCLASS_MACROS ) ) */ +/*--------------------------------------------------------------------------*/ +/*------------------------------ INCLUDES ----------------------------------*/ +/*--------------------------------------------------------------------------*/ + +#include "OPTUtils.h" + +/* OPTtypes.h defines standard interfaces for timing and random routines, as + well as the namespace OPTtypes_di_unipi_it and the macro + OPT_USE_NAMESPACES, useful for switching off all namespaces in one blow + for those strange cases where they create problems. */ + +#include +#include +#include +#include + +// QUICK HACK +#define throw(x) assert(false) + +/*--------------------------------------------------------------------------*/ +/*------------------------- NAMESPACE and USING ----------------------------*/ +/*--------------------------------------------------------------------------*/ + +#if( OPT_USE_NAMESPACES ) +namespace MCFClass_di_unipi_it +{ + /** @namespace MCFClass_di_unipi_it + The namespace MCFClass_di_unipi_it is defined to hold the MCFClass + class and all the relative stuff. It comprises the namespace + OPTtypes_di_unipi_it. */ + + using namespace OPTtypes_di_unipi_it; +#endif + +/*@} end( group( MCFCLASS_CONSTANTS ) ) */ +/*--------------------------------------------------------------------------*/ +/*-------------------------- CLASS MCFClass --------------------------------*/ +/*--------------------------------------------------------------------------*/ +/** @defgroup MCFCLASS_CLASSES Classes in MCFClass.h + @{ */ + +/*--------------------------------------------------------------------------*/ +/*--------------------------- GENERAL NOTES --------------------------------*/ +/*--------------------------------------------------------------------------*/ +/** This abstract base class defines a standard interface for (linear or + convex quadartic separable) Min Cost Flow (MCF) problem solvers. + + The data of the problem consist of a (directed) graph G = ( N , A ) with + n = |N| nodes and m = |A| (directed) arcs. Each node `i' has a deficit + b[ i ], i.e., the amount of flow that is produced/consumed by the node: + source nodes (which produce flow) have negative deficits and sink nodes + (which consume flow) have positive deficits. Each arc `(i, j)' has an + upper capacity U[ i , j ], a linear cost coefficient C[ i , j ] and a + (non negative) quadratic cost coefficient Q[ i , j ]. Flow variables + X[ i , j ] represents the amount of flow to be sent on arc (i, j). + Parallel arcs, i.e., multiple copies of the same arc `(i, j)' (with + possibily different costs and/or capacities) are in general allowed. + The formulation of the problem is therefore: + \f[ + \min \sum_{ (i, j) \in A } C[ i , j ] X[ i, j ] + + Q[ i , j ] X[ i, j ]^2 / 2 + \f] + \f[ + (1) \sum_{ (j, i) \in A } X[ j , i ] - + \sum_{ (i, j) \in A } X[ i , j ] = b[ i ] + \hspace{1cm} i \in N + \f] + \f[ + (2) 0 \leq X[ i , j ] \leq U[ i , j ] + \hspace{1cm} (i, j) \in A + \f] + The n equations (1) are the flow conservation constraints and the 2m + inequalities (2) are the flow nonnegativity and capacity constraints. + At least one of the flow conservation constraints is redundant, as the + demands must be balanced (\f$\sum_{ i \in N } b[ i ] = 0\f$); indeed, + exactly n - ConnectedComponents( G ) flow conservation constraints are + redundant, as demands must be balanced in each connected component of G. + Let us denote by QA and LA the disjoint subsets of A containing, + respectively, "quadratic" arcs (with Q[ i , j ] > 0) and "linear" arcs + (with Q[ i , j ] = 0); the (MCF) problem is linear if QA is empty, and + nonlinear (convex quadratic) if QA is nonempty. + + The dual of the problem is: + \f[ + \max \sum_{ i \in N } Pi[ i ] b[ i ] - + \sum_{ (i, j) \in A } W[ i , j ] U[ i , j ] - + \sum_{ (i, j) \in AQ } V[ i , j ]^2 / ( 2 * Q[ i , j ] ) + \f] + \f[ + (3.a) C[ i , j ] - Pi[ j ] + Pi[ i ] + W[ i , j ] - Z[ i , j ] = 0 + \hspace{1cm} (i, j) \in AL + \f] + \f[ + (3.b) C[ i , j ] - Pi[ j ] + Pi[ i ] + W[ i , j ] - Z[ i , j ] = + V[ i , j ] + \hspace{1cm} (i, j) \in AQ + \f] + \f[ + (4.a) W[ i , j ] \geq 0 \hspace{1cm} (i, j) \in A + \f] + \f[ + (4.b) Z[ i , j ] \geq 0 \hspace{1cm} (i, j) \in A + \f] + + Pi[] is said the vector of node potentials for the problem, W[] are + bound variables and Z[] are slack variables. Given Pi[], the quantities + \f[ + RC[ i , j ] = C[ i , j ] + Q[ i , j ] * X[ i , j ] - Pi[ j ] + Pi[ i ] + \f] + are said the "reduced costs" of arcs. + + A primal and dual feasible solution pair is optimal if and only if the + complementary slackness conditions + \f[ + RC[ i , j ] > 0 \Rightarrow X[ i , j ] = 0 + \f] + \f[ + RC[ i , j ] < 0 \Rightarrow X[ i , j ] = U[ i , j ] + \f] + are satisfied for all arcs (i, j) of A. + + The MCFClass class provides an interface with methods for managing and + solving problems of this kind. Actually, the class can also be used as + an interface for more general NonLinear MCF problems, where the cost + function either nonseparable ( C( X ) ) or arc-separable + ( \f$\sum_{ (i, j) \in A } C_{i,j}( X[ i, j ] )\f$ ). However, solvers + for NonLinear MCF problems are typically objective-function-specific, + and there is no standard way for inputting a nonlinear function different + from a separable convex quadratic one, so only the simplest form is dealt + with in the interface, leaving more complex NonLinear parts to the + interface of derived classes. */ + +class MCFClass { + +/*--------------------------------------------------------------------------*/ +/*----------------------- PUBLIC PART OF THE CLASS -------------------------*/ +/*--------------------------------------------------------------------------*/ +/*-- --*/ +/*-- The following methods and data are the actual interface of the --*/ +/*-- class: the standard user should use these methods and data only. --*/ +/*-- --*/ +/*--------------------------------------------------------------------------*/ + + public: + +/*--------------------------------------------------------------------------*/ +/*---------------------------- PUBLIC TYPES --------------------------------*/ +/*--------------------------------------------------------------------------*/ +/** @name Public types + The MCFClass defines four main public types: + + - Index, the type of arc and node indices; + + - FNumber, the type of flow variables, arc capacities, and node deficits; + + - CNumber, the type of flow costs, node potentials, and arc reduced costs; + + - FONumber, the type of objective function value. + + By re-defining the types in this section, most MCFSolver should be made + to work with any reasonable choice of data type (= one that is capable of + properly representing the data of the instances to be solved). This may + be relevant due to an important property of MCF problems: *if all arc + capacities and node deficits are integer, then there exists an integral + optimal primal solution*, and *if all arc costs are integer, then there + exists an integral optimal dual solution*. Even more importantly, *many + solution algorithms will in fact produce an integral primal/dual + solution for free*, because *every primal/dual solution they generate + during the solution process is naturally integral*. Therefore, one can + use integer data types to represent everything connected with flows and/or + costs if the corresponding data is integer in all instances one needs to + solve. This directly translates in significant memory savings and/or speed + improvements. + + *It is the user's responsibility to ensure that these types are set to + reasonable values*. So, the experienced user may want to experiment with + setting this data properly if memory footprint and/or speed is a primary + concern. Note, however, that *not all solution algorithms will happily + accept integer data*; one example are Interior-Point approaches, which + require both flow and cost variables to be continuous (float). So, the + viability of setting integer data (as well as its impact on performances) + is strictly related to the specific kind of algorithm used. Since these + types are common to all derived classes, they have to be set taking into + account the needs of all the solvers that are going to be used, and + adapting to the "worst case"; of course, FNumber == CNumber == double is + going to always be an acceptable "worst case" setting. MCFClass may in a + future be defined as a template class, with these as template parameters, + but this is currently deemed overkill and avoided. + + Finally, note that the above integrality property only holds for *linear* + MCF problems. If any arc has a nonzero quadratic cost coefficient, optimal + flows and potentials may be fractional even if all the data of the problem + (comprised quadratic cost coefficients) is integer. Hence, for *quadratic* + MCF solvers, a setting like FNumber == CNumber == double is actually + *mandatory*, for any reasonable algorithm will typically misbehave + otherwise. + @{ */ + +/*--------------------------------------------------------------------------*/ + + typedef unsigned int Index; ///< index of a node or arc ( >= 0 ) + typedef Index *Index_Set; ///< set (array) of indices + typedef const Index cIndex; ///< a read-only index + typedef cIndex *cIndex_Set; ///< read-only index array + +/*--------------------------------------------------------------------------*/ + + typedef int SIndex; ///< index of a node or arc + typedef SIndex *SIndex_Set; ///< set (array) of indices + typedef const SIndex cSIndex; ///< a read-only index + typedef cSIndex *cSIndex_Set; ///< read-only index array + +/*--------------------------------------------------------------------------*/ + + typedef double FNumber; ///< type of arc flow + typedef FNumber *FRow; ///< vector of flows + typedef const FNumber cFNumber; ///< a read-only flow + typedef cFNumber *cFRow; ///< read-only flow array + +/*--------------------------------------------------------------------------*/ + + typedef double CNumber; ///< type of arc flow cost + typedef CNumber *CRow; ///< vector of costs + typedef const CNumber cCNumber; ///< a read-only cost + typedef cCNumber *cCRow; ///< read-only cost array + +/*--------------------------------------------------------------------------*/ + + typedef double FONumber; + /**< type of the objective function: has to hold sums of products of + FNumber(s) by CNumber(s) */ + + typedef const FONumber cFONumber; ///< a read-only o.f. value + +/*--------------------------------------------------------------------------*/ +/** Very small class to simplify extracting the "+ infinity" value for a + basic type (FNumber, CNumber, Index); just use Inf(). */ + + template + class Inf { + public: + Inf() {} + operator T() { return( std::numeric_limits::max() ); } + }; + +/*--------------------------------------------------------------------------*/ +/** Very small class to simplify extracting the "machine epsilon" for a + basic type (FNumber, CNumber); just use Eps(). */ + + template + class Eps { + public: + Eps() {} + operator T() { return( std::numeric_limits::epsilon() ); } + }; + +/*--------------------------------------------------------------------------*/ +/** Small class for exceptions. Derives from std::exception implementing the + virtual method what() -- and since what is virtual, remember to always + catch it by reference (catch exception &e) if you want the thing to work. + MCFException class are thought to be of the "fatal" type, i.e., problems + for which no solutions exists apart from aborting the program. Other kinds + of exceptions can be properly handled by defining derived classes with + more information. */ + + class MCFException : public exception { + public: + MCFException( const char *const msg = 0 ) { errmsg = msg; } + + // wow, such a hack +#undef throw + const char* what( void ) const throw () { return( errmsg ); } +#define throw(x) assert(false) + private: + const char *errmsg; + }; + +/*--------------------------------------------------------------------------*/ +/** Public enum describing the possible parameters of the MCF solver, to be + used with the methods SetPar() and GetPar(). */ + + enum MCFParam { kMaxTime = 0 , ///< max time + kMaxIter , ///< max number of iteration + kEpsFlw , ///< tolerance for flows + kEpsDfct , ///< tolerance for deficits + kEpsCst , ///< tolerance for costs + kReopt , ///< whether or not to reoptimize + kLastParam /**< dummy parameter: this is used to + allow derived classes to "extend" + the set of parameters. */ + }; + +/*--------------------------------------------------------------------------*/ +/** Public enum describing the possible status of the MCF solver. */ + + enum MCFStatus { kUnSolved = -1 , ///< no solution available + kOK = 0 , ///< optimal solution found + + kStopped , ///< optimization stopped + kUnfeasible , ///< problem is unfeasible + kUnbounded , ///< problem is unbounded + kError ///< error in the solver + }; + +/*--------------------------------------------------------------------------*/ +/** Public enum describing the possible reoptimization status of the MCF + solver. */ + + enum MCFAnswer { kNo = 0 , ///< no + kYes ///< yes + }; + +/*--------------------------------------------------------------------------*/ +/** Public enum describing the possible file formats in WriteMCF(). */ + + enum MCFFlFrmt { kDimacs = 0 , ///< DIMACS file format for MCF + kQDimacs , ///< quadratic DIMACS file format for MCF + kMPS , ///< MPS file format for LP + kFWMPS ///< "Fixed Width" MPS format + }; + +/*--------------------------------------------------------------------------*/ +/** Base class for representing the internal state of the MCF algorithm. */ + + class MCFState { + public: + MCFState( void ) {}; + virtual ~MCFState() {}; + }; + + typedef MCFState *MCFStatePtr; ///< pointer to a MCFState + +/*@} -----------------------------------------------------------------------*/ +/*--------------------------- PUBLIC METHODS -------------------------------*/ +/*--------------------------------------------------------------------------*/ +/*---------------------------- CONSTRUCTOR ---------------------------------*/ +/*--------------------------------------------------------------------------*/ +/** @name Constructors + @{ */ + + MCFClass( cIndex nmx = 0 , cIndex mmx = 0 ) + { + nmax = nmx; + mmax = mmx; + n = m = 0; + + status = kUnSolved; + Senstv = true; + + EpsFlw = Eps() * 100; + EpsCst = Eps() * 100; + EpsDfct = EpsFlw * ( nmax ? nmax : 100 ); + + MaxTime = 0; + MaxIter = 0; + + MCFt = NULL; + } + +/**< Constructor of the class. + + nmx and mmx, if provided, are taken to be respectively the maximum number + of nodes and arcs in the network. If nonzero values are passed, memory + allocation can be anticipated in the constructor, which is sometimes + desirable. The maximum values are stored in the protected fields nmax and + mmax, and can be changed with LoadNet() [see below]; however, changing + them typically requires memory allocation/deallocation, which is + sometimes undesirable outside the constructor. + + After that an object has been constructed, no problem is loaded; this has + to be done with LoadNet() [see below]. Thus, it is an error to invoke any + method which requires the presence of a problem (typicall all except those + in the initializations part). The base class provides two protected fields + n and m for the current number of nodes and arcs, respectively, that are + set to 0 in the constructor precisely to indicate that no instance is + currently loaded. */ + +/*@} -----------------------------------------------------------------------*/ +/*-------------------------- OTHER INITIALIZATIONS -------------------------*/ +/*--------------------------------------------------------------------------*/ +/** @name Other initializations + @{ */ + + virtual void LoadNet( cIndex nmx = 0 , cIndex mmx = 0 , cIndex pn = 0 , + cIndex pm = 0 , cFRow pU = NULL , cCRow pC = NULL , + cFRow pDfct = NULL , cIndex_Set pSn = NULL , + cIndex_Set pEn = NULL ) = 0; + +/**< Inputs a new network. + + The parameters nmx and mmx are the new max number of nodes and arcs, + possibly overriding those set in the constructor [see above], altough at + the likely cost of memory allocation and deallocation. Passing nmx == + mmx == 0 is intended as a signal to the solver to deallocate everything + and wait for new orders; in this case, all the other parameters are ignored. + + Otherwise, in principle all the other parameters have to be provided. + Actually, some of them may not be needed for special classes of MCF + problems (e.g., costs in a MaxFlow problem, or start/end nodes in a + problem defined over a graph with fixed topology, such as a complete + graph). Also, passing NULL is allowed to set default values. + + The meaning of the parameters is the following: + + - pn is the current number of nodes of the network (<= nmax). + - pm is the number of arcs of the network (<= mmax). + + - pU is the m-vector of the arc upper capacities; capacities must be + nonnegative, but can in principle be infinite (== F_INF); passing + pU == NULL means that all capacities are infinite; + + - pC is the m-vector of the arc costs; costs must be finite (< C_INF); + passing pC == NULL means that all costs must be 0. + + - pDfct is the n-vector of the node deficits; source nodes have negative + deficits and sink nodes have positive deficits; passing pDfct == + NULL means that all deficits must be 0 (a circulation problem); + + - pSn is the m-vector of the arc starting nodes; pSn == NULL is in + principle not allowed, unless the topology of the graph is fixed; + - pEn is the m-vector of the arc ending nodes; same comments as for pSn. + + Note that node "names" in the arrays pSn and pEn must go from 1 to pn if + the macro USANAME0 [see above] is set to 0, while they must go from 0 to + pn - 1 if USANAME0 is set to 1. In both cases, however, the deficit of the + first node is read from the first (0-th) position of pDfct, that is if + USANAME0 == 0 then the deficit of the node with name `i' is read from + pDfct[ i - 1 ]. + + The data passed to LoadNet() can be used to specify that the arc `i' must + not "exist" in the problem. This is done by passing pC[ i ] == C_INF; + solvers which don't read costs are forced to read them in order to check + this, unless they provide alternative solver-specific ways to accomplish + the same tasks. These arcs are "closed", as for the effect of CloseArc() + [see below]. "invalid" costs (== C_INF) are set to 0 in order to being + subsequently capable of "opening" them back with OpenArc() [see below]. + The way in which these non-existent arcs are phisically dealt with is + solver-specific; in some solvers, for instance, this could be obtained by + simply putting their capacity to zero. Details about these issues should + be found in the interface of derived classes. + + Note that the quadratic part of the objective function, if any, is not + dealt with in LoadNet(); it can only be separately provided with + ChgQCoef() [see below]. By default, the problem is linear, i.e., all + coefficients of the second-order terms in the objective function are + assumed to be zero. */ + +/*--------------------------------------------------------------------------*/ + + virtual inline void LoadDMX( istream &DMXs , bool IsQuad = false ); + +/**< Read a MCF instance in DIMACS standard format from the istream. The + format is the following. The first line must be + + p min + + Then the node definition lines must be found, in the form + + n + + Not all nodes need have a node definition line; these are given zero + supply, i.e., they are transhipment nodes (supplies are the inverse of + deficits, i.e., a node with positive supply is a source node). Finally, + the arc definition lines must be found, in the form + + a + + There must be exactly arc definition lines in the file. + + This method is *not* pure virtual because an implementation is provided by + the base class, using the LoadNet() method (which *is* pure virtual). + However, the method *is* virtual to allow derived classes to implement + more efficient versions, should they have any reason to do so. + + \note Actually, the file format accepted by LoadDMX (at least in the + base class implementation) is more general than the DIMACS standard + format, in that it is allowed to mix node and arc definitions in + any order, while the DIMACS file requires all node information to + appear before all arc information. + + \note Other than for the above, this method is assumed to allow for + *quadratic* Dimacs files, encoding for convex quadratic separable + Min Cost Flow instances. This is a simple extension where each arc + descriptor has a sixth field, . The provided + istream is assumed to be quadratic Dimacs file if IsQuad is true, + and a regular linear Dimacs file otherwise. */ + +/*--------------------------------------------------------------------------*/ + + virtual void PreProcess( void ) {} + +/**< Extract a smaller/easier equivalent MCF problem. The data of the instance + is changed and the easier one is solved instead of the original one. In the + MCF case, preprocessing may involve reducing bounds, identifying + disconnected components of the graph etc. However, proprocessing is + solver-specific. + + This method can be implemented by derived classes in their solver-specific + way. Preprocessing may reveal unboundedness or unfeasibility of the + problem; if that happens, PreProcess() should properly set the `status' + field, that can then be read with MCFGetStatus() [see below]. + + Note that preprocessing may destroy all the solution information. Also, it + may be allowed to change the data of the problem, such as costs/capacities + of the arcs. + + A valid preprocessing is doing nothing, and that's what the default + implementation of this method (that is *not* pure virtual) does. */ + +/*--------------------------------------------------------------------------*/ + + virtual inline void SetPar( int par , int val ); + +/**< Set integer parameters of the algorithm. + + @param par is the parameter to be set; the enum MCFParam can be used, but + 'par' is an int (every enum is an int) so that the method can + be extended by derived classes for the setting of their + parameters + + @param value is the value to assign to the parameter. + + The base class implementation handles these parameters: + + - kMaxIter: the max number of iterations in which the MCF Solver can find + an optimal solution (default 0, which means no limit) + + - kReopt: tells the solver if it has to reoptimize. The implementation in + the base class sets a flag, the protected \c bool field \c + Senstv; if true (default) this field instructs the MCF solver to + to try to exploit the information about the latest optimal + solution to speedup the optimization of the current problem, + while if the field is false the MCF solver should restart the + optimization "from scratch" discarding any previous information. + Usually reoptimization speeds up the computation considerably, + but this is not always true, especially if the data of the + problem changes a lot.*/ + +/*--------------------------------------------------------------------------*/ + + virtual inline void SetPar( int par , double val ); + +/**< Set float parameters of the algorithm. + + @param par is the parameter to be set; the enum MCFParam can be used, but + 'par' is an int (every enum is an int) so that the method can + be extended by derived classes for the setting of their + parameters + + @param value is the value to assign to the parameter. + + The base class implementation handles these parameters: + + - kEpsFlw: sets the tolerance for controlling if the flow on an arc is zero + to val. This also sets the tolerance for controlling if a node + deficit is zero (see kEpsDfct) to val * < max number of nodes >; + this value should be safe for graphs in which any node has less + than < max number of nodes > adjacent nodes, i.e., for all graphs + but for very dense ones with "parallel arcs" + + - kEpsDfct: sets the tolerance for controlling if a node deficit is zero to + val, in case a better value than that autmatically set by + kEpsFlw (see above) is available (e.g., val * k would be good + if no node has more than k neighbours) + + - kEpsCst: sets the tolerance for controlling if the reduced cost of an arc + is zero to val. A feasible solution satisfying eps-complementary + slackness, i.e., such that + \f[ + RC[ i , j ] < - eps \Rightarrow X[ i , j ] = U[ ij ] + \f] + and + \f[ + RC[ i , j ] > eps \Rightarrow X[ i , j ] == 0 , + \f] + is known to be ( eps * n )-optimal. + + - kMaxTime: sets the max time (in seconds) in which the MCF Solver can find + an optimal solution (default 0, which means no limit). */ + +/*--------------------------------------------------------------------------*/ + + virtual inline void GetPar( int par , int &val ); + +/**< This method returns one of the integer parameter of the algorithm. + + @param par is the parameter to return [see SetPar( int ) for comments]; + + @param val upon return, it will contain the value of the parameter. + + The base class implementation handles the parameters kMaxIter and kReopt. + */ + +/*--------------------------------------------------------------------------*/ + + virtual inline void GetPar( int par , double &val ); + +/**< This method returns one of the integer parameter of the algorithm. + + @param par is the parameter to return [see SetPar( double ) for comments]; + + @param val upon return, it will contain the value of the parameter. + + The base class implementation handles the parameters kEpsFlw, kEpsDfct, + kEpsCst, and kMaxTime. */ + +/*--------------------------------------------------------------------------*/ + + virtual void SetMCFTime( bool TimeIt = true ) + { + if( TimeIt ) + if( MCFt ) + MCFt->ReSet(); + else + MCFt = new OPTtimers(); + else { + delete MCFt; + MCFt = NULL; + } + } + +/**< Allocate an OPTtimers object [see OPTtypes.h] to be used for timing the + methods of the class. The time can be read with TimeMCF() [see below]. By + default, or if SetMCFTime( false ) is called, no timing is done. Note that, + since all the relevant methods ot the class are pure virtual, MCFClass can + only manage the OPTtimers object, but it is due to derived classes to + actually implement the timing. + + @note time accumulates over the calls: calling SetMCFTime(), however, + resets the counters, allowing to time specific groups of calls. + + @note of course, setting kMaxTime [see SetPar() above] to any nonzero + value has no effect unless SetMCFTime( true ) has been called. */ + +/*@} -----------------------------------------------------------------------*/ +/*---------------------- METHODS FOR SOLVING THE PROBLEM -------------------*/ +/*--------------------------------------------------------------------------*/ +/** @name Solving the problem + @{ */ + + virtual void SolveMCF( void ) = 0; + +/**< Solver of the Min Cost Flow Problem. Attempts to solve the MCF instance + currently loaded in the object. */ + +/*--------------------------------------------------------------------------*/ + + inline int MCFGetStatus( void ) + { + return( status ); + } + +/**< Returns an int describing the current status of the MCF solver. Possible + return values are: + + - kUnSolved SolveMCF() has not been called yet, or the data of the + problem has been changed since the last call; + + - kOK optimization has been carried out succesfully; + + - kStopped optimization have been stopped before that the stopping + conditions of the solver applied, e.g. because of the + maximum allowed number of "iterations" [see SetPar( int )] + or the maximum allowed time [see SetPar( double )] has been + reached; this is not necessarily an error, as it might just + be required to re-call SolveMCF() giving it more "resources" + in order to solve the problem; + + - kUnfeasible if the current MCF instance is (primal) unfeasible; + + - kUnbounded if the current MCF instance is (primal) unbounded (this can + only happen if the solver actually allows F_INF capacities, + which is nonstandard in the interface); + + - kError if there was an error during the optimization; this + typically indicates that computation cannot be resumed, + although solver-dependent ways of dealing with + solver-dependent errors may exist. + + MCFClass has a protected \c int \c member \c status \c that can be used by + derived classes to hold status information and that is returned by the + standard implementation of this method. Note that \c status \c is an + \c int \c and not an \c enum \c, and that an \c int \c is returned by this + method, in order to allow the derived classes to extend the set of return + values if they need to do so. */ + +/*@} -----------------------------------------------------------------------*/ +/*---------------------- METHODS FOR READING RESULTS -----------------------*/ +/*--------------------------------------------------------------------------*/ +/** @name Reading flow solution + @{ */ + + virtual void MCFGetX( FRow F , Index_Set nms = NULL , + cIndex strt = 0 , Index stp = Inf() ) = 0; + +/**< Write the optimal flow solution in the vector F[]. If nms == NULL, F[] + will be in "dense" format, i.e., the flow relative to arc `i' + (i in 0 .. m - 1) is written in F[ i ]. If nms != NULL, F[] will be in + "sparse" format, i.e., the indices of the nonzero elements in the flow + solution are written in nms (that is then Inf()-terminated) and + the flow value of arc nms[ i ] is written in F[ i ]. Note that nms is + *not* guaranteed to be ordered. Also, note that, unlike MCFGetRC() and + MCFGetPi() [see below], nms is an *output* of the method. + + The parameters `strt' and `stp' allow to restrict the output of the method + to all and only the arcs `i' with strt <= i < min( MCFm() , stp ). */ + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + virtual cFRow MCFGetX( void ) + { + return( NULL ); + } + +/**< Return a read-only pointer to an internal data structure containing the + flow solution in "dense" format. Since this may *not always be available*, + depending on the implementation, this method can (uniformly) return NULL. + This is done by the base class already, so a derived class that does not + have the information ready does not need to implement the method. */ + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + virtual bool HaveNewX( void ) + { + return( false ); + } + +/**< Return true if a different (approximately) optimal primal solution is + available. If the method returns true, then any subsequent call to (any + form of) MCFGetX() will return a different primal solution w.r.t. the one + that was being returned *before* the call to HaveNewX(). This solution need + not be optimal (although, ideally, it has to be "good); this can be checked + by comparing its objective function value, that will be returned by a call + to MCFGetFO() [see below]. + + Any subsequent call of HaveNewX() that returns true produces a new + solution, until the first that returns false; from then on, no new + solutions will be generated until something changes in the problem's + data. + + Note that a default implementation of HaveNewX() is provided which is + good for those solvers that only produce one optimal primal solution. */ + +/*@} -----------------------------------------------------------------------*/ +/** @name Reading potentials + @{ */ + + virtual void MCFGetPi( CRow P , cIndex_Set nms = NULL , + cIndex strt = 0 , Index stp = Inf() ) = 0; + +/**< Writes the optimal node potentials in the vector P[]. If nms == NULL, + the node potential of node `i' (i in 0 .. n - 1) is written in P[ i ] + (note that here node names always start from zero, regardless to the value + of USENAME0). If nms != NULL, it must point to a vector of indices in + 0 .. n - 1 (ordered in increasing sense and Inf()-terminated), and + the node potential of nms[ i ] is written in P[ i ]. Note that, unlike + MCFGetX() above, nms is an *input* of the method. + + The parameters `strt' and `stp' allow to restrict the output of the method + to all and only the nodes `i' with strt <= i < min( MCFn() , stp ). `strt' + and `stp' work in "&&" with nms; that is, if nms != NULL then only the + values corresponding to nodes which are *both* in nms[] and whose index is + in the correct range are returned. */ + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + virtual cCRow MCFGetPi( void ) + { + return( NULL ); + } + +/**< Return a read-only pointer to an internal data structure containing the + node potentials. Since this may *not always be available*, depending on + the implementation, this method can (uniformly) return NULL. + This is done by the base class already, so a derived class that does not + have the information ready does not need to implement the method. */ + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + virtual bool HaveNewPi( void ) + { + return( false ); + } + +/**< Return true if a different (approximately) optimal dual solution is + available. If the method returns true, then any subsequent call to (any + form of) MCFGetPi() will return a different dual solution, and MCFGetRC() + [see below] will return the corresponding reduced costs. The new solution + need not be optimal (although, ideally, it has to be "good); this can be + checked by comparing its objective function value, that will be returned + by a call to MCFGetDFO() [see below]. + + Any subsequent call of HaveNewPi() that returns true produces a new + solution, until the first that returns false; from then on, no new + solutions will be generated until something changes in the problem's + data. + + Note that a default implementation of HaveNewPi() is provided which is + good for those solvers that only produce one optimal dual solution. */ + +/*@} -----------------------------------------------------------------------*/ +/** @name Reading reduced costs + @{ */ + + virtual void MCFGetRC( CRow CR , cIndex_Set nms = NULL , + cIndex strt = 0 , Index stp = Inf() ) = 0; + +/**< Write the reduced costs corresponding to the current dual solution in + RC[]. If nms == NULL, the reduced cost of arc `i' (i in 0 .. m - 1) is + written in RC[ i ]; if nms != NULL, it must point to a vector of indices + in 0 .. m - 1 (ordered in increasing sense and Inf()-terminated), + and the reduced cost of arc nms[ i ] is written in RC[ i ]. Note that, + unlike MCFGetX() above, nms is an *input* of the method. + + The parameters `strt' and `stp' allow to restrict the output of the method + to all and only the arcs `i' with strt <= i < min( MCFm() , stp ). `strt' + and `stp' work in "&&" with nms; that is, if nms != NULL then only the + values corresponding to arcs which are *both* in nms[] and whose index is + in the correct range are returned. + + @note the output of MCFGetRC() will change after any call to HaveNewPi() + [see above] which returns true. */ + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + virtual cCRow MCFGetRC( void ) + { + return( NULL ); + } + +/**< Return a read-only pointer to an internal data structure containing the + reduced costs. Since this may *not always be available*, depending on the + implementation, this method can (uniformly) return NULL. + This is done by the base class already, so a derived class that does not + have the information ready does not need to implement the method. + + @note the output of MCFGetRC() will change after any call to HaveNewPi() + [see above] which returns true. */ + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + virtual CNumber MCFGetRC( cIndex i ) = 0; + +/**< Return the reduced cost of the i-th arc. This information should be + cheapily available in most implementations. + + @note the output of MCFGetRC() will change after any call to HaveNewPi() + [see above] which returns true. */ + +/*@} -----------------------------------------------------------------------*/ +/** @name Reading the objective function value + @{ */ + + virtual FONumber MCFGetFO( void ) = 0; + +/**< Return the objective function value of the primal solution currently + returned by MCFGetX(). + + If MCFGetStatus() == kOK, this is guaranteed to be the optimal objective + function value of the problem (to within the optimality tolerances), but + only prior to any call to HaveNewX() that returns true. MCFGetFO() + typically returns Inf() if MCFGetStatus() == kUnfeasible and + - Inf() if MCFGetStatus() == kUnbounded. If MCFGetStatus() == + kStopped and MCFGetFO() returns a finite value, it must be an upper bound + on the optimal objective function value (typically, the objective function + value of one primal feasible solution). */ + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + virtual FONumber MCFGetDFO( void ) + { + switch( MCFGetStatus() ) { + case( kUnSolved ): + case( kStopped ): + case( kError ): return( -Inf() ); + default: return( MCFGetFO() ); + } + } + +/**< Return the objective function value of the dual solution currently + returned by MCFGetPi() / MCFGetRC(). This value (possibly) changes after + any call to HaveNewPi() that returns true. The relations between + MCFGetStatus() and MCFGetDFO() are analogous to these of MCFGetFO(), + except that a finite value corresponding to kStopped must be a lower + bound on the optimal objective function value (typically, the objective + function value one dual feasible solution). + + A default implementation is provided for MCFGetDFO(), which is good for + MCF solvers where the primal and dual optimal solution values always + are identical (except if the problem is unfeasible/unbounded). */ + +/*@} -----------------------------------------------------------------------*/ +/** @name Getting unfeasibility certificate + @{ */ + + virtual FNumber MCFGetUnfCut( Index_Set Cut ) + { + *Cut = Inf(); + return( 0 ); + } + +/**< Return an unfeasibility certificate. In an unfeasible MCF problem, + unfeasibility can always be reduced to the existence of a cut (subset of + nodes of the graph) such as either: + + - the inverse of the deficit of the cut (the sum of all the deficits of the + nodes in the cut) is larger than the forward capacity of the cut (sum of + the capacities of forward arcs in the cut); that is, the nodes in the + cut globally produce more flow than can be routed to sinks outside the + cut; + + - the deficit of the cut is larger than the backward capacity of the cut + (sum of the capacities of backward arcs in the cut); that is, the nodes + in the cut globally require more flow than can be routed to them from + sources outside the cut. + + When detecting unfeasibility, MCF solvers are typically capable to provide + one such cut. This information can be useful - typically, the only way to + make the problem feasible is to increase the capacity of at least one of + the forward/backward arcs of the cut -, and this method is provided for + getting it. It can be called only if MCFGetStatus() == kUnfeasible, and + should write in Cut the set of names of nodes in the unfeasible cut (note + that node names depend on USENAME0), Inf()-terminated, returning the + deficit of the cut (which allows to distinguish which of the two cases + above hold). In general, no special properties can be expected from the + returned cut, but solvers should be able to provide e.g. "small" cuts. + + However, not all solvers may be (easily) capable of providing this + information; thus, returning 0 (no cut) is allowed, as in the base class + implementation, to signify that this information is not available. */ + +/*@} -----------------------------------------------------------------------*/ +/** @name Getting unboundedness certificate + @{ */ + + virtual Index MCFGetUnbCycl( Index_Set Pred , Index_Set ArcPred ) + { + return( Inf() ); + } + +/**< Return an unboundedness certificate. In an unbounded MCF problem, + unboundedness can always be reduced to the existence of a directed cycle + with negative cost and all arcs having infinite capacity. + When detecting unboundedness, MCF solvers are typically capable to provide + one such cycle. This information can be useful, and this method is provided + for getting it. It can be called only if MCFGetStatus() == kUnbounded, and + writes in Pred[] and ArcPred[], respectively, the node and arc predecessor + function of the cycle. That is, if node `i' belongs to the cycle then + `Pred[ i ]' contains the name of the predecessor of `j' of `i' in the cycle + (note that node names depend on USENAME0), and `ArcPred[ i ]' contains the + index of the arc joining the two (note that in general there may be + multiple copies of each arc). Entries of the vectors for nodes not + belonging to the cycle are in principle undefined, and the name of one node + belonging to the cycle is returned by the method. + Note that if there are multiple cycles with negative costs this method + will return just one of them (finding the cycle with most negative cost + is an NO-hard problem), although solvers should be able to produce cycles + with "large negative" cost. + + However, not all solvers may be (easily) capable of providing this + information; thus, returning Inf() is allowed, as in the base class + implementation, to signify that this information is not available. */ + +/*@} -----------------------------------------------------------------------*/ +/** @name Saving/restoring the state of the solver + @{ */ + + virtual MCFStatePtr MCFGetState( void ) + { + return( NULL ); + } + + /**< Save the state of the MCF solver. The MCFClass interface supports the + notion of saving and restoring the state of the MCF solver, such as the + current/optimal basis in a simplex solver. The "empty" class MCFState is + defined as a placeholder for state descriptions. + + MCFGetState() creates and returns a pointer to an object of (a proper + derived class of) class MCFState which describes the current state of the + MCF solver. */ + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + virtual void MCFPutState( MCFStatePtr S ) {} + +/**< Restore the solver to the state in which it was when the state `S' was + created with MCFGetState() [see above]. + + The typical use of this method is the following: a MCF problem is solved + and the "optimal state" is set aside. Then the problem changes and it is + re-solved. Then, the problem has to be changed again to a form that is + close to the original one but rather different from the second one (think + of a long backtracking in a Branch & Bound) to which the current "state" + referes. Then, the old optimal state can be expected to provide a better + starting point for reoptimization [see ReOptimize() below]. + + Note, however, that the state is only relative to the optimization process, + i.e., this operation is meaningless if the data of the problem has changed + in the meantime. So, if a state has to be used for speeding up + reoptimization, the following has to be done: + + - first, the data of the solver is brought back to *exactly* the same as + it was at the moment where the state `S' was created (prior than this + operation a ReOptimize( false ) call is probably advisable); + + - then, MCFPutState() is called (and ReOptimize( true ) is called); + + - only afterwards the data of the problem is changed to the final state + and the problem is solved. + + A "put state" operation does not "deplete" the state, which can therefore + be used more than once. Indeed, a state is constructed inside the solver + for each call to MCFGetState(), but the solver never deletes statuses; + this has to be done on the outside when they are no longer needed (the + solver must be "resistent" to deletion of the state at any moment). + + Since not all the MCF solvers reoptimize (efficiently enough to make these + operations worth), an "empty" implementation that does nothing is provided + by the base class. */ + +/*@} -----------------------------------------------------------------------*/ +/** @name Time the code + @{ */ + + void TimeMCF( double &t_us , double &t_ss ) + { + t_us = t_ss = 0; + if( MCFt ) + MCFt->Read( t_us , t_ss ); + } + +/**< Time the code. If called within any of the methods of the class that are + "actively timed" (this depends on the subclasses), this method returns the + user and sistem time (in seconds) since the start of that method. If + methods that are actively timed call other methods that are actively + timed, TimeMCF() returns the (...) time since the beginning of the + *outer* actively timed method. If called outside of any actively timed + method, this method returns the (...) time spent in all the previous + executions of all the actively timed methods of the class. + + Implementing the proper calls to MCFt->Start() and MCFt->Stop() is due to + derived classes; these should at least be placed at the beginning and at + the end, respectively, of SolveMCF() and presumably the Chg***() methods, + that is, at least these methods should be "actively timed". */ + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + double TimeMCF( void ) + { + return( MCFt ? MCFt->Read() : 0 ); + } + +/**< Like TimeMCF(double,double) [see above], but returns the total time. */ + +/*@} -----------------------------------------------------------------------*/ +/** @name Check the solutions + @{ */ + + inline void CheckPSol( void ); + +/**< Check that the primal solution returned by the solver is primal feasible. + (to within the tolerances set by SetPar(kEps****) [see above], if any). Also, + check that the objective function value is correct. + + This method is implemented by the base class, using the above methods + for collecting the solutions and the methods of the next section for + reading the data of the problem; as such, they will work for any derived + class that properly implements all these methods. */ + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + inline void CheckDSol( void ); + +/**< Check that the dual solution returned by the solver is dual feasible. + (to within the tolerances set by SetPar(kEps****) [see above], if any). Also, + check that the objective function value is correct. + + This method is implemented by the base class, using the above methods + for collecting the solutions and the methods of the next section for + reading the data of the problem; as such, they will work for any derived + class that properly implements all these methods. */ + +/*@} -----------------------------------------------------------------------*/ +/*-------------- METHODS FOR READING THE DATA OF THE PROBLEM ---------------*/ +/*--------------------------------------------------------------------------*/ +/** @name Reading graph size + @{ */ + + inline Index MCFnmax( void ) + { + return( nmax ); + } + +/**< Return the maximum number of nodes for this instance of MCFClass. + The implementation of the method in the base class returns the protected + fields \c nmax, which is provided for derived classes to hold this + information. */ + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + inline Index MCFmmax( void ) + { + return( mmax ); + } + +/**< Return the maximum number of arcs for this instance of MCFClass. + The implementation of the method in the base class returns the protected + fields \c mmax, which is provided for derived classes to hold this + information. */ + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + inline Index MCFn( void ) + { + return( n ); + } + +/**< Return the number of nodes in the current graph. + The implementation of the method in the base class returns the protected + fields \c n, which is provided for derived classes to hold this + information. */ + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + inline Index MCFm( void ) + { + return( m ); + } + +/**< Return the number of arcs in the current graph. + The implementation of the method in the base class returns the protected + fields \c m, which is provided for derived classes to hold this + information. */ + +/*@} -----------------------------------------------------------------------*/ +/** @name Reading graph topology + @{ */ + + virtual void MCFArcs( Index_Set Startv , Index_Set Endv , + cIndex_Set nms = NULL , cIndex strt = 0 , + Index stp = Inf() ) = 0; + +/**< Write the starting (tail) and ending (head) nodes of the arcs in Startv[] + and Endv[]. If nms == NULL, then the information relative to all arcs is + written into Startv[] and Endv[], otherwise Startv[ i ] and Endv[ i ] + contain the information relative to arc nms[ i ] (nms[] must be + Inf()-terminated). + + The parameters `strt' and `stp' allow to restrict the output of the method + to all and only the arcs `i' with strt <= i < min( MCFm() , stp ). `strt' + and `stp' work in "&&" with nms; that is, if nms != NULL then only the + values corresponding to arcs which are *both* in nms and whose index is in + the correct range are returned. + + Startv or Endv can be NULL, meaning that only the other information is + required. + + @note If USENAME0 == 0 then the returned node names will be in the range + 1 .. n, while if USENAME0 == 1 the returned node names will be in + the range 0 .. n - 1. + + @note If the graph is "dynamic", be careful to use MCFn() e MCFm() to + properly choose the dimension of nodes and arcs arrays. */ + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + virtual Index MCFSNde( cIndex i ) = 0; + +/**< Return the starting (tail) node of the arc `i'. + + @note If USENAME0 == 0 then the returned node names will be in the range + 1 .. n, while if USENAME0 == 1 the returned node names will be in + the range 0 .. n - 1. */ + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + virtual Index MCFENde( cIndex i ) = 0; + +/**< Return the ending (head) node of the arc `i'. + + @note If USENAME0 == 0 then the returned node names will be in the range + 1 .. n, while if USENAME0 == 1 the returned node names will be in + the range 0 .. n - 1. */ + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + virtual cIndex_Set MCFSNdes( void ) + { + return( NULL ); + } + +/**< Return a read-only pointer to an internal vector containing the starting + (tail) nodes for each arc. Since this may *not always be available*, + depending on the implementation, this method can (uniformly) return NULL. + This is done by the base class already, so a derived class that does not + have the information ready does not need to implement the method. */ + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + virtual cIndex_Set MCFENdes( void ) + { + return( NULL ); + } + +/**< Return a read-only pointer to an internal vector containing the ending + (head) nodes for each arc. Since this may *not always be available*, + depending on the implementation, this method can (uniformly) return NULL. + This is done by the base class already, so a derived class that does not + have the information ready does not need to implement the method. */ + +/*@} -----------------------------------------------------------------------*/ +/** @name Reading arc costs + @{ */ + + virtual void MCFCosts( CRow Costv , cIndex_Set nms = NULL , + cIndex strt = 0 , Index stp = Inf() ) = 0; + +/**< Write the arc costs into Costv[]. If nms == NULL, then all the costs are + written, otherwise Costv[ i ] contains the information relative to arc + nms[ i ] (nms[] must be Inf()-terminated). + + The parameters `strt' and `stp' allow to restrict the output of the method + to all and only the arcs `i' with strt <= i < min( MCFm() , stp ). `strt' + and `stp' work in "&&" with nms; that is, if nms != NULL then only the + values corresponding to arcs which are *both* in nms and whose index is in + the correct range are returned. */ + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + virtual CNumber MCFCost( cIndex i ) = 0; + +/**< Return the cost of the i-th arc. */ + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + virtual cCRow MCFCosts( void ) + { + return( NULL ); + } + +/**< Return a read-only pointer to an internal vector containing the arc + costs. Since this may *not always be available*, depending on the + implementation, this method can (uniformly) return NULL. + This is done by the base class already, so a derived class that does not + have the information ready does not need to implement the method. */ + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + virtual void MCFQCoef( CRow Qv , cIndex_Set nms = NULL , + cIndex strt = 0 , Index stp = Inf() ) + +/**< Write the quadratic coefficients of the arc costs into Qv[]. If + nms == NULL, then all the costs are written, otherwise Costv[ i ] contains + the information relative to arc nms[ i ] (nms[] must be + Inf()-terminated). + + The parameters `strt' and `stp' allow to restrict the output of the method + to all and only the arcs `i' with strt <= i < min( MCFm() , stp ). `strt' + and `stp' work in "&&" with nms; that is, if nms != NULL then only the + values corresponding to arcs which are *both* in nms and whose index is in + the correct range are returned. + + Note that the method is *not* pure virtual: an implementation is provided + for "pure linear" MCF solvers that only work with all zero quadratic + coefficients. */ + { + if( nms ) { + while( *nms < strt ) + nms++; + + for( Index h ; ( h = *(nms++) ) < stp ; ) + *(Qv++) = 0; + } + else { + if( stp > m ) + stp = m; + + while( stp-- > strt ) + *(Qv++) = 0; + } + } + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + virtual CNumber MCFQCoef( cIndex i ) + +/**< Return the quadratic coefficients of the cost of the i-th arc. Note that + the method is *not* pure virtual: an implementation is provided for "pure + linear" MCF solvers that only work with all zero quadratic coefficients. */ + { + return( 0 ); + } + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + virtual cCRow MCFQCoef( void ) + { + return( NULL ); + } + +/**< Return a read-only pointer to an internal vector containing the arc + costs. Since this may *not always be available*, depending on the + implementation, this method can (uniformly) return NULL. + This is done by the base class already, so a derived class that does not + have the information ready (such as "pure linear" MCF solvers that only + work with all zero quadratic coefficients) does not need to implement the + method. */ + +/*@} -----------------------------------------------------------------------*/ +/** @name Reading arc capacities + @{ */ + + virtual void MCFUCaps( FRow UCapv , cIndex_Set nms = NULL , + cIndex strt = 0 , Index stp = Inf() ) = 0; + +/**< Write the arc capacities into UCapv[]. If nms == NULL, then all the + capacities are written, otherwise UCapv[ i ] contains the information + relative to arc nms[ i ] (nms[] must be Inf()-terminated). + + The parameters `strt' and `stp' allow to restrict the output of the method + to all and only the arcs `i' with strt <= i < min( MCFm() , stp ). `strt' + and `stp' work in "&&" with nms; that is, if nms != NULL then only the + values corresponding to arcs which are *both* in nms and whose index is in + the correct range are returned. */ + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + virtual FNumber MCFUCap( cIndex i ) = 0; + +/**< Return the capacity of the i-th arc. */ + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + virtual cFRow MCFUCaps( void ) + { + return( NULL ); + } + +/**< Return a read-only pointer to an internal vector containing the arc + capacities. Since this may *not always be available*, depending on the + implementation, this method can (uniformly) return NULL. + This is done by the base class already, so a derived class that does not + have the information ready does not need to implement the method. */ + +/*@} -----------------------------------------------------------------------*/ +/** @name Reading node deficits + @{ */ + + virtual void MCFDfcts( FRow Dfctv , cIndex_Set nms = NULL , + cIndex strt = 0 , Index stp = Inf() ) = 0; + +/**< Write the node deficits into Dfctv[]. If nms == NULL, then all the + defcits are written, otherwise Dfctvv[ i ] contains the information + relative to node nms[ i ] (nms[] must be Inf()-terminated). + + The parameters `strt' and `stp' allow to restrict the output of the method + to all and only the nodes `i' with strt <= i < min( MCFn() , stp ). `strt' + and `stp' work in "&&" with nms; that is, if nms != NULL then only the + values corresponding to nodes which are *both* in nms and whose index is in + the correct range are returned. + + @note Here node "names" (strt and stp, those contained in nms[] or `i' + in MCFDfct()) go from 0 to n - 1, regardless to the value of + USENAME0; hence, if USENAME0 == 0 then the first node is "named 1", + but its deficit is returned by MCFDfcts( Dfctv , NULL , 0 , 1 ). */ + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + virtual FNumber MCFDfct( cIndex i ) = 0; + +/**< Return the deficit of the i-th node. + + @note Here node "names" go from 0 to n - 1, regardless to the value of + USENAME0; hence, if USENAME0 == 0 then the first node is "named 1", + but its deficit is returned by MCFDfct( 0 ). */ + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + virtual cFRow MCFDfcts( void ) + { + return( NULL ); + } + +/**< Return a read-only pointer to an internal vector containing the node + deficits. Since this may *not always be available*, depending on the + implementation, this method can (uniformly) return NULL. + This is done by the base class already, so a derived class that does not + have the information ready does not need to implement the method. + + @note Here node "names" go from 0 to n - 1, regardless to the value of + USENAME0; hence, if USENAME0 == 0 then the first node is "named 1", + but its deficit is contained in MCFDfcts()[ 0 ]. */ + +/*@} -----------------------------------------------------------------------*/ +/** @name Write problem to file + @{ */ + + virtual inline void WriteMCF( ostream &oStrm , int frmt = 0 ); + +/**< Write the current MCF problem to an ostream. This may be useful e.g. for + debugging purposes. + + The base MCFClass class provides output in two different formats, depending + on the value of the parameter frmt: + + - kDimacs the problem is written in DIMACS standard format, read by most + MCF codes available; + + - kMPS the problem is written in the "modern version" (tab-separated) + of the MPS format, read by most LP/MIP solvers; + + - kFWMPS the problem is written in the "old version" (fixed width + fields) of the MPS format; this is read by most LP/MIP + solvers, but some codes still require the old format. + + The implementation of WriteMCF() in the base class uses all the above + methods for reading the data; as such it will work for any derived class + that properly implements this part of the interface, but it may not be + very efficient. Thus, the method is virtual to allow the derived classes + to either re-implement WriteMCF() for the above two formats in a more + efficient way, and/or to extend it to support other solver-specific + formats. + + @note None of the above two formats supports quadratic MCFs, so if + nonzero quadratic coefficients are present, they are just ignored. + */ + +/*@} -----------------------------------------------------------------------*/ +/*------------- METHODS FOR ADDING / REMOVING / CHANGING DATA --------------*/ +/*--------------------------------------------------------------------------*/ +/** @name Changing the costs + @{ */ + + virtual void ChgCosts( cCRow NCost , cIndex_Set nms = NULL , + cIndex strt = 0 , Index stp = Inf() ) = 0; + +/**< Change the arc costs. In particular, change the costs that are: + + - listed in into the vector of indices `nms' (ordered in increasing + sense and Inf()-terminated), + + - *and* whose name belongs to the interval [`strt', `stp'). + + That is, if strt <= nms[ i ] < stp, then the nms[ i ]-th cost will be + changed to NCost[ i ]. If nms == NULL (as the default), *all* the + entries in the given range will be changed; if stp > MCFm(), then the + smaller bound is used. + + @note changing the costs of arcs that *do not exist* is *not allowed*; + only arcs which have not been closed/deleted [see CloseArc() / + DelArc() below and LoadNet() above about C_INF costs] can be + touched with these methods. */ + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + virtual void ChgCost( Index arc , cCNumber NCost ) = 0; + +/**< Change the cost of the i-th arc. + + @note changing the costs of arcs that *do not exist* is *not allowed*; + only arcs which have not been closed/deleted [see CloseArc() / + DelArc() below and LoadNet() above about C_INF costs] can be + touched with these methods. */ + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + virtual void ChgQCoef( cCRow NQCoef = NULL , cIndex_Set nms = NULL , + cIndex strt = 0 , Index stp = Inf() ) + +/**< Change the quadratic coefficients of the arc costs. In particular, + change the coefficients that are: + + - listed in into the vector of indices `nms' (ordered in increasing + sense and Inf()-terminated), + + - *and* whose name belongs to the interval [`strt', `stp'). + + That is, if strt <= nms[ i ] < stp, then the nms[ i ]-th cost will be + changed to NCost[ i ]. If nms == NULL (as the default), *all* the + entries in the given range will be changed; if stp > MCFm(), then the + smaller bound is used. If NQCoef == NULL, all the specified coefficients + are set to zero. + + Note that the method is *not* pure virtual: an implementation is provided + for "pure linear" MCF solvers that only work with all zero quadratic + coefficients. + + @note changing the costs of arcs that *do not exist* is *not allowed*; + only arcs which have not been closed/deleted [see CloseArc() / + DelArc() below and LoadNet() above about C_INF costs] can be + touched with these methods. */ + { + assert(NQCoef); + } + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + virtual void ChgQCoef( Index arc , cCNumber NQCoef ) + +/**< Change the quadratic coefficient of the cost of the i-th arc. + + Note that the method is *not* pure virtual: an implementation is provided + for "pure linear" MCF solvers that only work with all zero quadratic + coefficients. + + @note changing the costs of arcs that *do not exist* is *not allowed*; + only arcs which have not been closed/deleted [see CloseArc() / + DelArc() below and LoadNet() above about C_INF costs] can be + touched with these methods. */ + { + assert(NQCoef); + } + +/*@} -----------------------------------------------------------------------*/ +/** @name Changing the capacities + @{ */ + + virtual void ChgUCaps( cFRow NCap , cIndex_Set nms = NULL , + cIndex strt = 0 , Index stp = Inf() ) = 0; + +/**< Change the arc capacities. In particular, change the capacities that are: + + - listed in into the vector of indices `nms' (ordered in increasing sense + and Inf()-terminated), + + - *and* whose name belongs to the interval [`strt', `stp'). + + That is, if strt <= nms[ i ] < stp, then the nms[ i ]-th capacity will be + changed to NCap[ i ]. If nms == NULL (as the default), *all* the + entries in the given range will be changed; if stp > MCFm(), then the + smaller bound is used. + + @note changing the capacities of arcs that *do not exist* is *not allowed*; + only arcs that have not been closed/deleted [see CloseArc() / + DelArc() below and LoadNet() above about C_INF costs] can be + touched with these methods. */ + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + virtual void ChgUCap( Index arc , cFNumber NCap ) = 0; + +/**< Change the capacity of the i-th arc. + + @note changing the capacities of arcs that *do not exist* is *not allowed*; + only arcs that have not been closed/deleted [see CloseArc() / + DelArc() below and LoadNet() above about C_INF costs] can be + touched with these methods. */ + +/*@} -----------------------------------------------------------------------*/ +/** @name Changing the deficits + @{ */ + + virtual void ChgDfcts( cFRow NDfct , cIndex_Set nms = NULL , + cIndex strt = 0 , Index stp = Inf() ) = 0; + +/**< Change the node deficits. In particular, change the deficits that are: + + - listed in into the vector of indices `nms' (ordered in increasing sense + and Inf()-terminated), + + - *and* whose name belongs to the interval [`strt', `stp'). + + That is, if strt <= nms[ i ] < stp, then the nms[ i ]-th deficit will be + changed to NDfct[ i ]. If nms == NULL (as the default), *all* the + entries in the given range will be changed; if stp > MCFn(), then the + smaller bound is used. + + Note that, in ChgDfcts(), node "names" (strt, stp or those contained + in nms[]) go from 0 to n - 1, regardless to the value of USENAME0; hence, + if USENAME0 == 0 then the first node is "named 1", but its deficit can be + changed e.g. with ChgDfcts( &new_deficit , NULL , 0 , 1 ). + + @note changing the capacities of nodes that *do not exist* is *not + allowed*; only nodes that have not been deleted [see DelNode() + below] can be touched with these methods. */ + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + virtual void ChgDfct( Index node , cFNumber NDfct ) = 0; + +/**< Change the deficit of the i-th node. + + Note that, in ChgDfct[s](), node "names" (i, strt/ stp or those contained + in nms[]) go from 0 to n - 1, regardless to the value of USENAME0; hence, + if USENAME0 == 0 then the first node is "named 1", but its deficit can be + changed e.g. with ChgDfcts( 0 , new_deficit ). + + @note changing the capacities of nodes that *do not exist* is *not + allowed*; only nodes that have not been deleted [see DelNode() + below] can be touched with these methods. */ + +/*@} -----------------------------------------------------------------------*/ +/** @name Changing graph topology + @{ */ + + virtual void CloseArc( cIndex name ) = 0; + +/**< "Close" the arc `name'. Although all the associated information (name, + cost, capacity, end and start node) is kept, the arc is removed from the + problem until OpenArc( i ) [see below] is called. + + "closed" arcs always have 0 flow, but are otherwise counted as any other + arc; for instance, MCFm() does *not* decrease as an effect of a call to + CloseArc(). How this closure is implemented is solver-specific. */ + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + virtual bool IsClosedArc( cIndex name ) = 0; + +/**< IsClosedArc() returns true if and only if the arc `name' is closed. */ + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + virtual void DelNode( cIndex name ) = 0; + +/**< Delete the node `name'. + + For any value of `name', all incident arcs to that node are closed [see + CloseArc() above] (*not* Deleted, see DelArc() below) and the deficit is + set to zero. + + Il furthermore `name' is the last node, the number of nodes as reported by + MCFn() is reduced by at least one, until the n-th node is not a deleted + one. */ + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + virtual void OpenArc( cIndex name ) = 0; + +/**< Restore the previously closed arc `name'. It is an error to open an arc + that has not been previously closed. */ + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + virtual Index AddNode( cFNumber aDfct ) = 0; + +/**< Add a new node with deficit aDfct, returning its name. Inf() is + returned if there is no room for a new node. Remember that the node names + are either { 0 .. nmax - 1 } or { 1 .. nmax }, depending on the value of + USENAME0. */ + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + virtual void ChangeArc( cIndex name , cIndex nSN = Inf() , + cIndex nEN = Inf() ) = 0; + +/**< Change the starting and/or ending node of arc `name' to nSN and nEN. + Each parameter being Inf() means to leave the previous starting or + ending node untouched. When this method is called `name' can be either the + name of a "normal" arc or that of a "closed" arc [see CloseArc() above]: + in the latter case, at the end of ChangeArc() the arc is *still closed*, + and it remains so until OpenArc( name ) [see above] is called. */ + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + virtual void DelArc( cIndex name ) = 0; + +/**< Delete the arc `name'. Unlike "closed" arcs, all the information + associated with a deleted arc is lost and `name' is made available as a + name for new arcs to be created with AddArc() [see below]. + + Il furthermore `name' is the last arc, the number of arcs as reported by + MCFm() is reduced by at least one, until the m-th arc is not a deleted + one. Otherwise, the flow on the arc is always ensured to be 0. */ + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + virtual bool IsDeletedArc( cIndex name ) = 0; + +/**< Return true if and only if the arc `name' is deleted. It should only + be called with name < MCFm(), as every other arc is deleted by + definition. */ + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + virtual Index AddArc( cIndex Start , cIndex End , cFNumber aU , + cCNumber aC ) = 0; + +/**< Add the new arc ( Start , End ) with cost aC and capacity aU, + returning its name. Inf() is returned if there is no room for a new + arc. Remember that arc names go from 0 to mmax - 1. */ + +/*@} -----------------------------------------------------------------------*/ +/*------------------------------ DESTRUCTOR --------------------------------*/ +/*--------------------------------------------------------------------------*/ +/** @name Destructor + @{ */ + + virtual ~MCFClass() + { + delete MCFt; + } + +/**< Destructor of the class. The implementation in the base class only + deletes the MCFt field. It is virtual, as it should be. */ + +/*@} -----------------------------------------------------------------------*/ +/*-------------------- PROTECTED PART OF THE CLASS -------------------------*/ +/*--------------------------------------------------------------------------*/ +/*-- --*/ +/*-- The following fields are believed to be general enough to make it --*/ +/*-- worth adding them to the abstract base class. --*/ +/*-- --*/ +/*--------------------------------------------------------------------------*/ + + protected: + +/*--------------------------------------------------------------------------*/ +/*--------------------------- PROTECTED METHODS ----------------------------*/ +/*--------------------------------------------------------------------------*/ +/*-------------------------- MANAGING COMPARISONS --------------------------*/ +/*--------------------------------------------------------------------------*/ +/** @name Managing comparisons. + The following methods are provided for making it easier to perform + comparisons, with and without tolerances. + @{ */ + +/*--------------------------------------------------------------------------*/ +/** true if flow x is equal to zero (possibly considering tolerances). */ + + template + inline bool ETZ( T x , const T eps ) + { + if( numeric_limits::is_integer ) + return( x == 0 ); + else + return( (x <= eps ) && ( x >= -eps ) ); + } + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ +/** true if flow x is greater than zero (possibly considering tolerances). */ + + template + inline bool GTZ( T x , const T eps ) + { + if( numeric_limits::is_integer ) + return( x > 0 ); + else + return( x > eps ); + } + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ +/** true if flow x is greater than or equal to zero (possibly considering + tolerances). */ + + template + inline bool GEZ( T x , const T eps ) + { + if( numeric_limits::is_integer ) + return( x >= 0 ); + else + return( x >= - eps ); + } + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ +/** true if flow x is less than zero (possibly considering tolerances). */ + + template + inline bool LTZ( T x , const T eps ) + { + if( numeric_limits::is_integer ) + return( x < 0 ); + else + return( x < - eps ); + } + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ +/** true if flow x is less than or equal to zero (possibly considering + tolerances). */ + + template + inline bool LEZ( T x , const T eps ) + { + if( numeric_limits::is_integer ) + return( x <= 0 ); + else + return( x <= eps ); + } + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ +/** true if flow x is greater than flow y (possibly considering tolerances). + */ + + template + inline bool GT( T x , T y , const T eps ) + { + if( numeric_limits::is_integer ) + return( x > y ); + else + return( x > y + eps ); + } + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ +/** true if flow x is less than flow y (possibly considering tolerances). */ + + template + inline bool LT( T x , T y , const T eps ) + { + if( numeric_limits::is_integer ) + return( x < y ); + else + return( x < y - eps ); + } + +/*@} -----------------------------------------------------------------------*/ +/*---------------------- PROTECTED DATA STRUCTURES -------------------------*/ +/*--------------------------------------------------------------------------*/ + + Index n; ///< total number of nodes + Index nmax; ///< maximum number of nodes + + Index m; ///< total number of arcs + Index mmax; ///< maximum number of arcs + + int status; ///< return status, see the comments to MCFGetStatus() above. + ///< Note that the variable is defined int to allow derived + ///< classes to return their own specialized status codes + bool Senstv; ///< true <=> the latest optimal solution should be exploited + + OPTtimers *MCFt; ///< timer for performances evaluation + + FNumber EpsFlw; ///< precision for comparing arc flows / capacities + FNumber EpsDfct; ///< precision for comparing node deficits + CNumber EpsCst; ///< precision for comparing arc costs + + double MaxTime; ///< max time (in seconds) in which MCF Solver can find + ///< an optimal solution (0 = no limits) + int MaxIter; ///< max number of iterations in which MCF Solver can find + ///< an optimal solution (0 = no limits) + +/*--------------------------------------------------------------------------*/ + + }; // end( class MCFClass ) + +/*@} -----------------------------------------------------------------------*/ +/*------------------- inline methods implementation ------------------------*/ +/*--------------------------------------------------------------------------*/ + +inline void MCFClass::LoadDMX( istream &DMXs , bool IsQuad ) +{ + // read first non-comment line - - - - - - - - - - - - - - - - - - - - - - - + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + char c; + for(;;) { + if( ! ( DMXs >> c ) ) + throw( MCFException( "LoadDMX: error reading the input stream" ) ); + + if( c != 'c' ) // if it's not a comment + break; + + do // skip the entire line + if( ! DMXs.get( c ) ) + throw( MCFException( "LoadDMX: error reading the input stream" ) ); + while( c != '\n' ); + } + + if( c != 'p' ) + throw( MCFException( "LoadDMX: format error in the input stream" ) ); + + char buf[ 3 ]; // free space + DMXs >> buf; // skip (in has to be "min") + + Index tn; + if( ! ( DMXs >> tn ) ) + throw( MCFException( "LoadDMX: error reading number of nodes" ) ); + + Index tm; + if( ! ( DMXs >> tm ) ) + throw( MCFException( "LoadDMX: error reading number of arcs" ) ); + + // allocate memory - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + FRow tU = new FNumber[ tm ]; // arc upper capacities + FRow tDfct = new FNumber[ tn ]; // node deficits + Index_Set tStartn = new Index[ tm ]; // arc start nodes + Index_Set tEndn = new Index[ tm ]; // arc end nodes + CRow tC = new CNumber[ tm ]; // arc costs + CRow tQ = NULL; + if( IsQuad ) + tQ = new CNumber[ tm ]; // arc quadratic costs + + + for( Index i = 0 ; i < tn ; ) // all deficits are 0 + tDfct[ i++ ] = 0; // unless otherwise stated + + // read problem data - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + Index i = 0; // arc counter + for(;;) { + if( ! ( DMXs >> c ) ) + break; + + switch( c ) { + case( 'c' ): // comment line- - - - - - - - - - - - - - - - - - - - - - - + do + if( ! DMXs.get( c ) ) + throw( MCFException( "LoadDMX: error reading the input stream" ) ); + while( c != '\n' ); + break; + + case( 'n' ): // description of a node - - - - - - - - - - - - - - - - - - + Index j; + if( ! ( DMXs >> j ) ) + throw( MCFException( "LoadDMX: error reading node name" ) ); + + if( ( j < 1 ) || ( j > tn ) ) + throw( MCFException( "LoadDMX: invalid node name" ) ); + + FNumber Dfctj; + if( ! ( DMXs >> Dfctj ) ) + throw( MCFException( "LoadDMX: error reading deficit" ) ); + + tDfct[ j - 1 ] -= Dfctj; + break; + + case( 'a' ): // description of an arc - - - - - - - - - - - - - - - - - - + if( i == tm ) + throw( MCFException( "LoadDMX: too many arc descriptors" ) ); + + if( ! ( DMXs >> tStartn[ i ] ) ) + throw( MCFException( "LoadDMX: error reading start node" ) ); + + if( ( tStartn[ i ] < 1 ) || ( tStartn[ i ] > tn ) ) + throw( MCFException( "LoadDMX: invalid start node" ) ); + + if( ! ( DMXs >> tEndn[ i ] ) ) + throw( MCFException( "LoadDMX: error reading end node" ) ); + + if( ( tEndn[ i ] < 1 ) || ( tEndn[ i ] > tn ) ) + throw( MCFException( "LoadDMX: invalid end node" ) ); + + if( tStartn[ i ] == tEndn[ i ] ) + throw( MCFException( "LoadDMX: self-loops not permitted" ) ); + + FNumber LB; + if( ! ( DMXs >> LB ) ) + throw( MCFException( "LoadDMX: error reading lower bound" ) ); + + if( ! ( DMXs >> tU[ i ] ) ) + throw( MCFException( "LoadDMX: error reading upper bound" ) ); + + if( ! ( DMXs >> tC[ i ] ) ) + throw( MCFException( "LoadDMX: error reading arc cost" ) ); + + if( tQ ) { + if( ! ( DMXs >> tQ[ i ] ) ) + throw( MCFException( "LoadDMX: error reading arc quadratic cost" ) ); + + if( tQ[ i ] < 0 ) + throw( MCFException( "LoadDMX: negative arc quadratic cost" ) ); + } + + if( tU[ i ] < LB ) + throw( MCFException( "LoadDMX: lower bound > upper bound" ) ); + + tU[ i ] -= LB; + tDfct[ tStartn[ i ] - 1 ] += LB; + tDfct[ tEndn[ i ] - 1 ] -= LB; + #if( USENAME0 ) + tStartn[ i ]--; // in the DIMACS format, node names start from 1 + tEndn[ i ]--; + #endif + i++; + break; + + default: // invalid code- - - - - - - - - - - - - - - - - - - - - - - - - + throw( MCFException( "LoadDMX: invalid code" ) ); + + } // end( switch( c ) ) + } // end( for( ever ) ) + + if( i < tm ) + throw( MCFException( "LoadDMX: too few arc descriptors" ) ); + + // call LoadNet- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + LoadNet( tn , tm , tn , tm , tU , tC , tDfct , tStartn , tEndn ); + + // then pass quadratic costs, if any + + if( tQ ) + ChgQCoef( tQ ); + + // delete the original data structures - - - - - - - - - - - - - - - - - - - + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + delete[] tQ; + delete[] tC; + delete[] tEndn; + delete[] tStartn; + delete[] tDfct; + delete[] tU; + + } // end( MCFClass::LoadDMX ) + +/*--------------------------------------------------------------------------*/ + +inline void MCFClass::SetPar( int par , int val ) +{ + switch( par ) { + case( kMaxIter ): + MaxIter = val; + break; + + case( kReopt ): + Senstv = (val == kYes); + break; + + default: + throw( MCFException( "Error using SetPar: unknown parameter" ) ); + } + } + +/*--------------------------------------------------------------------------*/ + +inline void MCFClass::SetPar( int par, double val ) +{ + switch( par ) { + case( kEpsFlw ): + if( EpsFlw != FNumber( val ) ) { + EpsFlw = FNumber( val ); + EpsDfct = EpsFlw * ( nmax ? nmax : 100 ); + status = kUnSolved; + } + break; + + case( kEpsDfct ): + if( EpsDfct != FNumber( val ) ) { + EpsDfct = FNumber( val ); + status = kUnSolved; + } + break; + + case( kEpsCst ): + if( EpsCst != CNumber( val ) ) { + EpsCst = CNumber( val ); + status = kUnSolved; + } + break; + + case( kMaxTime ): + MaxTime = val; + break; + + default: + throw( MCFException( "Error using SetPar: unknown parameter" ) ); + } + } + +/*--------------------------------------------------------------------------*/ + +inline void MCFClass::GetPar( int par, int &val ) +{ + switch( par ) { + case( kMaxIter ): + val = MaxIter; + break; + + case( kReopt ): + if( Senstv ) val = kYes; + else val = kNo; + break; + + default: + throw( MCFException( "Error using GetPar: unknown parameter" ) ); + } + } + +/*--------------------------------------------------------------------------*/ + +inline void MCFClass::GetPar( int par , double &val ) +{ + switch( par ) { + case( kEpsFlw ): + val = double( EpsFlw ); + break; + + case( kEpsDfct ): + val = double( EpsDfct ); + break; + + case( kEpsCst ): + val = double( EpsCst ); + break; + + case( kMaxTime ): + val = MaxTime; + break; + + default: + throw( MCFException( "Error using GetPar: unknown parameter" ) ); + } + } + +/*--------------------------------------------------------------------------*/ + +inline void MCFClass::CheckPSol( void ) +{ + FRow tB = new FNumber[ MCFn() ]; + MCFDfcts( tB ); + #if( ! USENAME0 ) + tB--; + #endif + + FRow tX = new FNumber[ MCFm() ]; + MCFGetX( tX ); + FONumber CX = 0; + for( Index i = 0 ; i < MCFm() ; i++ ) + if( GTZ( tX[ i ] , EpsFlw ) ) { + if( IsClosedArc( i ) ) + throw( MCFException( "Closed arc with nonzero flow (CheckPSol)" ) ); + + if( GT( tX[ i ] , MCFUCap( i ) , EpsFlw ) ) + throw( MCFException( "Arc flow exceeds capacity (CheckPSol)" ) ); + + if( LTZ( tX[ i ] , EpsFlw ) ) + throw( MCFException( "Arc flow is negative (CheckPSol)" ) ); + + CX += ( FONumber( MCFCost( i ) ) + + FONumber( MCFQCoef( i ) ) * FONumber( tX[ i ] ) / 2 ) + * FONumber( tX[ i ] ); + tB[ MCFSNde( i ) ] += tX[ i ]; + tB[ MCFENde( i ) ] -= tX[ i ]; + } + delete[] tX; + #if( ! USENAME0 ) + tB++; + #endif + + for( Index i = 0 ; i < MCFn() ; i++ ) + if( ! ETZ( tB[ i ] , EpsDfct ) ) + throw( MCFException( "Node is not balanced (CheckPSol)" ) ); + + delete[] tB; + CX -= MCFGetFO(); + if( ( CX >= 0 ? CX : - CX ) > EpsCst * MCFn() ) + throw( MCFException( "Objective function value is wrong (CheckPSol)" ) ); + + } // end( CheckPSol ) + +/*--------------------------------------------------------------------------*/ + +inline void MCFClass::CheckDSol( void ) +{ + CRow tPi = new CNumber[ MCFn() ]; + MCFGetPi( tPi ); + + FONumber BY = 0; + for( Index i = 0 ; i < MCFn() ; i++ ) + BY += FONumber( tPi[ i ] ) * FONumber( MCFDfct( i ) ); + + CRow tRC = new CNumber[ MCFm() ]; + MCFGetRC( tRC ); + + FRow tX = new FNumber[ MCFm() ]; + MCFGetX( tX ); + + #if( ! USENAME0 ) + tPi--; + #endif + + FONumber QdrtcCmpnt = 0; // the quadratic part of the objective + for( Index i = 0 ; i < MCFm() ; i++ ) { + if( IsClosedArc( i ) ) + continue; + + cFONumber tXi = FONumber( tX[ i ] ); + cCNumber QCi = CNumber( MCFQCoef( i ) * tXi ); + QdrtcCmpnt += QCi * tXi; + CNumber RCi = MCFCost( i ) + QCi + + tPi[ MCFSNde( i ) ] - tPi[ MCFENde( i ) ]; + RCi -= tRC[ i ]; + + if( ! ETZ( RCi , EpsCst ) ) + throw( MCFException( "Reduced Cost value is wrong (CheckDSol)" ) ); + + if( LTZ( tRC[ i ] , EpsCst ) ) { + BY += FONumber( tRC[ i ] ) * FONumber( MCFUCap( i ) ); + + if( LT( tX[ i ] , MCFUCap( i ) , EpsFlw ) ) + throw( MCFException( "Csomplementary Slackness violated (CheckDSol)" ) ); + } + else + if( GTZ( tRC[ i ] , EpsCst ) && GTZ( tX[ i ] , EpsFlw ) ) + throw( MCFException( "Complementary Slackness violated (CheckDSol)" ) ); + + } // end( for( i ) ) + + delete[] tX; + delete[] tRC; + #if( ! USENAME0 ) + tPi++; + #endif + delete[] tPi; + + BY -= ( MCFGetFO() + QdrtcCmpnt / 2 ); + if( ( BY >= 0 ? BY : - BY ) > EpsCst * MCFn() ) + throw( MCFException( "Objective function value is wrong (CheckDSol)" ) ); + + } // end( CheckDSol ) + +/*--------------------------------------------------------------------------*/ + +inline void MCFClass::WriteMCF( ostream &oStrm , int frmt ) +{ + if( ( ! numeric_limits::is_integer ) || + ( ! numeric_limits::is_integer ) ) + oStrm.precision( 12 ); + + switch( frmt ) { + case( kDimacs ): // DIMACS standard format - - - - - - - - - - - - - - - - + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - + case( kQDimacs ): // ... linear or quadratic + + // compute and output preamble and size- - - - - - - - - - - - - - - - - - + + oStrm << "p min " << MCFn() << " "; + { + Index tm = MCFm(); + for( Index i = MCFm() ; i-- ; ) + if( IsClosedArc( i ) || IsDeletedArc( i ) ) + tm--; + + oStrm << tm << endl; + } + + // output arc information- - - - - - - - - - - - - - - - - - - - - - - - - + + for( Index i = 0 ; i < MCFm() ; i++ ) + if( ( ! IsClosedArc( i ) ) && ( ! IsDeletedArc( i ) ) ) { + oStrm << "a\t"; + #if( USENAME0 ) + oStrm << MCFSNde( i ) + 1 << "\t" << MCFENde( i ) + 1 << "\t"; + #else + oStrm << MCFSNde( i ) << "\t" << MCFENde( i ) << "\t"; + #endif + oStrm << "0\t" << MCFUCap( i ) << "\t" << MCFCost( i ); + + if( frmt == kQDimacs ) + oStrm << "\t" << MCFQCoef( i ); + + oStrm << endl; + } + + // output node information - - - - - - - - - - - - - - - - - - - - - - - - + + for( Index i = 0 ; i < MCFn() ; ) { + cFNumber Dfcti = MCFDfct( i++ ); + if( Dfcti ) + oStrm << "n\t" << i << "\t" << - Dfcti << endl; + } + + break; + + case( kMPS ): // MPS format(s)- - - - - - - - - - - - - - - - - - - - - + case( kFWMPS ): //- - - - - - - - - - - - - - - - - - - - - - - - - - - - + + // writing problem name- - - - - - - - - - - - - - - - - - - - - - - - - - + + oStrm << "NAME MCF" << endl; + + // writing ROWS section: flow balance constraints- - - - - - - - - - - - - + + oStrm << "ROWS" << endl << " N obj" << endl; + + for( Index i = 0 ; i < MCFn() ; ) + oStrm << " E c" << i++ << endl; + + // writing COLUMNS section - - - - - - - - - - - - - - - - - - - - - - - - + + oStrm << "COLUMNS" << endl; + + if( frmt == kMPS ) { // "modern" MPS + for( Index i = 0 ; i < MCFm() ; i++ ) + if( ( ! IsClosedArc( i ) ) && ( ! IsDeletedArc( i ) ) ) { + oStrm << " x" << i << "\tobj\t" << MCFCost( i ) << "\tc"; + #if( USENAME0 ) + oStrm << MCFSNde( i ) << "\t-1" << endl << " x" << i << "\tc" + << MCFENde( i ) << "\t1" << endl; + #else + oStrm << MCFSNde( i ) - 1 << "\t-1" << endl << " x" << i << "\tc" + << MCFENde( i ) - 1 << "\t1" << endl; + #endif + } + } + else // "old" MPS + for( Index i = 0 ; i < MCFm() ; i++ ) { + ostringstream myField; + myField << "x" << i << ends; + oStrm << " " << setw( 8 ) << left << myField.str() + << " " << setw( 8 ) << left << "obj" + << " " << setw( 12 ) << left << MCFCost( i ); + + myField.seekp( 0 ); + myField << "c" << MCFSNde( i ) - ( 1 - USENAME0 ) << ends; + + oStrm << " " << setw( 8 ) << left << myField.str() + << " " << setw( 12 ) << left << -1 << endl; + + myField.seekp( 0 ); + myField << "x" << i << ends; + + oStrm << " " << setw( 8 ) << left << myField.str(); + + myField.seekp( 0 ); + myField << "c" << MCFENde( i ) - ( 1 - USENAME0 ) << ends; + + oStrm << " " << setw( 8 ) << left << myField.str() << endl; + } + + // writing RHS section: flow balance constraints - - - - - - - - - - - - - + + oStrm << "RHS" << endl; + + if( frmt == kMPS ) // "modern" MPS + for( Index i = 0 ; i < MCFn() ; i++ ) + oStrm << " rhs\tc" << i << "\t" << MCFDfct( i ) << endl; + else // "old" MPS + for( Index i = 0 ; i < MCFn() ; i++ ) { + ostringstream myField; + oStrm << " " << setw( 8 ) << left << "rhs"; + myField << "c" << i << ends; + + oStrm << " " << setw( 8 ) << left << myField.str() + << " " << setw( 12 ) << left << MCFDfct( i ) << endl; + } + + // writing BOUNDS section- - - - - - - - - - - - - - - - - - - - - - - - - + + oStrm << "BOUNDS" << endl; + + if( frmt == kMPS ) { // "modern" MPS + for( Index i = 0 ; i < MCFm() ; i++ ) + if( ( ! IsClosedArc( i ) ) && ( ! IsDeletedArc( i ) ) ) + oStrm << " UP BOUND\tx" << i << "\t" << MCFUCap( i ) << endl; + } + else // "old" MPS + for( Index i = 0 ; i < MCFm() ; i++ ) + if( ( ! IsClosedArc( i ) ) && ( ! IsDeletedArc( i ) ) ) { + ostringstream myField; + oStrm << " " << setw( 2 ) << left << "UP" + << " " << setw( 8 ) << left << "BOUND"; + + myField << "x" << i << ends; + + oStrm << " " << setw( 8 ) << left << myField.str() + << " " << setw( 12 ) << left << MCFUCap( i ) << endl; + } + + oStrm << "ENDATA" << endl; + break; + + default: // unknown format - - - - - - - - - - - - - - - - - - - - + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - + oStrm << "Error: unknown format " << frmt << endl; + } + } // end( MCFClass::WriteMCF ) + +/*--------------------------------------------------------------------------*/ + +#if( OPT_USE_NAMESPACES ) + }; // end( namespace MCFClass_di_unipi_it ) +#endif + +/*--------------------------------------------------------------------------*/ +/*--------------------------------------------------------------------------*/ + +#undef throw + +#endif /* MCFClass.h included */ + +/*--------------------------------------------------------------------------*/ +/*------------------------- End File MCFClass.h ----------------------------*/ +/*--------------------------------------------------------------------------*/ diff --git a/impl/MCFSimplex.cpp b/impl/MCFSimplex.cpp new file mode 100644 index 0000000..60c70d1 --- /dev/null +++ b/impl/MCFSimplex.cpp @@ -0,0 +1,4198 @@ +/*--------------------------------------------------------------------------*/ +/*---------------------------- File MCFSimplex.C ---------------------------*/ +/*--------------------------------------------------------------------------*/ +/*-- --*/ +/*-- Linear and Quadratic Min Cost Flow problems solver based on the --*/ +/*-- (primal and dual) simplex algorithm. Conforms to the standard MCF --*/ +/*-- interface defined in MCFClass.h. --*/ +/*-- --*/ +/*-- VERSION 1.00 --*/ +/*-- 29 - 08 - 2011 --*/ +/*-- --*/ +/*-- Implementation: --*/ +/*-- --*/ +/*-- Alessandro Bertolini --*/ +/*-- Antonio Frangioni --*/ +/*-- --*/ +/*-- Operations Research Group --*/ +/*-- Dipartimento di Informatica --*/ +/*-- Universita' di Pisa --*/ +/*-- --*/ +/*-- Copyright (C) 2008 - 2011 by Alessandro Bertolini, Antonio Frangioni --*/ +/*-- --*/ +/*--------------------------------------------------------------------------*/ +/*--------------------------------------------------------------------------*/ +/*--------------------------- IMPLEMENTATION -------------------------------*/ +/*--------------------------------------------------------------------------*/ +/*--------------------------------------------------------------------------*/ + +/*--------------------------------------------------------------------------*/ +/*------------------------------ INCLUDES ----------------------------------*/ +/*--------------------------------------------------------------------------*/ + +#include "MCFSimplex.h" +#include +#include + +#include +#include + +/*--------------------------------------------------------------------------*/ +/*-------------------------------- USING -----------------------------------*/ +/*--------------------------------------------------------------------------*/ + +#if( OPT_USE_NAMESPACES ) +using namespace MCFClass_di_unipi_it; +#endif + +/*--------------------------------------------------------------------------*/ +/*-------------------------------- MACROS ----------------------------------*/ +/*--------------------------------------------------------------------------*/ + +#define LIMITATEPRECISION 1 + +/* If LIMITATEPRECISION is 1, in the quadratic case the Primal Simplex accepts + entering arc in base only if the decrease of the o.f. value is bigger than + a fixed thresold (EpsOpt * oldFOValue / n). Otherwise, any strict decrease + in the o.f. value is accepted. */ + +#define UNIPI_PRIMAL_INITIAL_SHOW 0 + +/* If UNIPI_PRIMAL_INITIAL_SHOW = 1, Primal Simplex shows the initial condition + (arcs and nodes) of the network. */ + +#define UNIPI_PRIMAL_ITER_SHOW 0 + +/* If UNIPI_PRIMAL_FINAL_SHOW = x with x > 0, Primal Simplex shows the condition + (arcs and nodes) of the network every x iterations. */ + +#define UNIPI_PRIMAL_FINAL_SHOW 0 + +/* If UNIPI_PRIMAL_FINAL_SHOW = 1, Primal Simplex shows the final condition + (arcs and nodes) of the network. */ + +#define UNIPI_DUAL_INITIAL_SHOW 0 + +/* If UNIPI_DUAL_INITIAL_SHOW = 1, Dual Simplex shows the initial condition + (arcs and nodes) of the network. */ + +#define UNIPI_DUAL_ITER_SHOW 0 + +/* If UNIPI_DUAL_FINAL_SHOW = x with x > 0, Dual Simplex shows the condition + (arcs and nodes) of the network every x iterations. */ + +#define UNIPI_DUAL_FINAL_SHOW 0 + +/* If UNIPI_DUAL_FINAL_SHOW = 1, Dual Simplex shows the final condition + (arcs and nodes) of the network. */ + +#define UNIPI_VIS_DUMMY_ARCS 1 + +/* If UNIPI_VIS_DUMMY_ARCS = 1, Primal Simplex or Dual Simplex shows the + conditions of the dummy arcs. */ + +#define UNIPI_VIS_ARC_UPPER 1 +#define UNIPI_VIS_ARC_COST 1 +#define UNIPI_VIS_ARC_Q_COST 1 +#define UNIPI_VIS_ARC_REDUCT_COST 1 +#define UNIPI_VIS_ARC_STATE 1 +#define UNIPI_VIS_NODE_BASIC_ARC 1 + +/* When Primal Simplex or Dual Simplex shows the conditions of the network, + for every arcs the algorithm shows the flow, for every nodes it shows + balance and potential. These 6 flags decide if the algorithm shows a + particular value of the arcs/nodes; for example if + UNIPI_VIS_ARC_UPPER == 1, the algorithm shows the upper bounds of all + arcs. */ + +#define FOSHOW 0 + +/* If FOSHOW is 1, the algorithm shows the f.o. value every x iterations + (x = UNIPI_PRIMAL_ITER_SHOW or x = UNIPI_DUAL_ITER_SHOW). */ + +#define OPTQUADRATIC 0 + +/* If OPTQUADRATIC is 1 the Primal Simplex, in the quadratic case, tries to + optimize the update of the potential. + Unfortunately this doesn't work well: for this reason it is set to 0. */ + +#ifndef throw +// QUICK HACK +#define throw(x) assert(false); +#endif + +/*--------------------------------------------------------------------------*/ +/*--------------------------- FUNCTIONS ------------------------------------*/ +/*--------------------------------------------------------------------------*/ + +template +inline T ABS( const T x ) +{ + return( x >= T( 0 ) ? x : - x ); + } + +/*--------------------------------------------------------------------------*/ + +template +inline void Swap( T &v1 , T &v2 ) +{ + T temp = v1; + v1 = v2; + v2 = temp; + } + +/*--------------------------------------------------------------------------*/ +/*--------------------------- CONSTANTS ------------------------------------*/ +/*--------------------------------------------------------------------------*/ + +#if( QUADRATICCOST == 0 ) + static const char DELETED = -2; // ident for deleted arcs + static const char CLOSED = -1; // ident for closed arcs + static const char BASIC = 0; // ident for basis arcs + static const char AT_LOWER = 1; // ident for arcs in L + static const char AT_UPPER = 2; // ident for arcs in U +#endif + +/* These macros will be used by method MemAllocCandidateList() to set the + values of numCandidateList and hotListSize. There are different macros, + according to: + + - the used Simplex + - the size of the network + - (obviously) the different variables + + This set of values tries to improve the performance of the two algorithms + according to diversified situations. */ + +static const int PRIMAL_LOW_NUM_CANDIDATE_LIST = 30; +static const int PRIMAL_MEDIUM_NUM_CANDIDATE_LIST = 50; +static const int PRIMAL_HIGH_NUM_CANDIDATE_LIST = 200; +static const int PRIMAL_LOW_HOT_LIST_SIZE = 5; +static const int PRIMAL_MEDIUM_HOT_LIST_SIZE = 10; +static const int PRIMAL_HIGH_HOT_LIST_SIZE = 20; +static const int DUAL_LOW_NUM_CANDIDATE_LIST = 6; +static const int DUAL_HIGH_NUM_CANDIDATE_LIST = 10; +static const int DUAL_LOW_HOT_LIST_SIZE = 1; +static const int DUAL_HIGH_HOT_LIST_SIZE = 2; + + + +/*--------------------------------------------------------------------------*/ +/*--------------------------- COSTRUCTOR -----------------------------------*/ +/*--------------------------------------------------------------------------*/ + +MCFSimplex::MCFSimplex( cIndex nmx , cIndex mmx ) + : + MCFClass( nmx , mmx ) +{ + #if( QUADRATICCOST ) + if( numeric_limits::is_integer ) + throw( MCFException( "FNumber must be float if QUADRATICCOST == 1" ) ); + + if( numeric_limits::is_integer ) + throw( MCFException( "CNumber must be float if QUADRATICCOST == 1" ) ); + #endif + + usePrimalSimplex = true; + + newSession = true; + if( nmax && mmax ) + MemAlloc(); + else + nmax = mmax = 0; + + #if( QUADRATICCOST ) + recomputeFOLimits = 100; + // recomputeFOLimits represents the limit of the iteration in which + // quadratic Primal Simplex computes "manually" the f.o. value + EpsOpt = 1e-13; + // EpsOpt is the fixed precision of the quadratic Primal Simplex + #endif + + pricingRule = kCandidateListPivot; + forcedNumCandidateList = 0; + forcedHotListSize = 0; + usePrimalSimplex = true; + nodesP = NULL; + nodesD = NULL; + arcsP = NULL; + arcsD = NULL; + candP = NULL; + candD = NULL; + + if( numeric_limits::is_integer ) + MAX_ART_COST = CNumber( 1e7 ); + else + MAX_ART_COST = CNumber( 1e10 ); + + } // end( MCFSimplex ) + +/*--------------------------------------------------------------------------*/ +/*-------------------------- OTHER INITIALIZATIONS -------------------------*/ +/*--------------------------------------------------------------------------*/ + +void MCFSimplex::LoadNet( cIndex nmx , cIndex mmx , cIndex pn , cIndex pm , + cFRow pU , cCRow pC , cFRow pDfct , + cIndex_Set pSn , cIndex_Set pEn ) +{ + MemDeAllocCandidateList(); + + if( ( nmx != nmax ) || ( mmx != mmax ) ) { + // if the size of the allocated memory changes + + if( nmax && mmax ) { // if the memory was already allocated + MemDeAlloc(true); // deallocate the Primal + MemDeAlloc(false); // and the Dual data structures + nmax = mmax = 0; + } + + if( nmx && mmx ) { // if the new dimensions of the memory are correct + nmax = nmx; + mmax = mmx; + MemAlloc(); + } + } + + if( ( ! nmax ) || ( ! mmax ) ) + // if one of the two dimension of the memory isn't correct + nmax = mmax = 0; + + if( nmax ) { // if the new dimensions of the memory are correct + n = pn; + m = pm; + + if( usePrimalSimplex ) { + stopNodesP = nodesP + n; + dummyRootP = nodesP + nmax; + for( nodePType *node = nodesP ; node != stopNodesP ; node++ ) + node->balance = pDfct[ node - nodesP ]; // initialize nodes + + stopArcsP = arcsP + m; + dummyArcsP = arcsP + mmax; + stopDummyP = dummyArcsP + n; + for( arcPType *arc = arcsP ; arc != stopArcsP ; arc++ ) { + // initialize real arcs + if (pC) + arc->cost = pC[ arc - arcsP ]; + else + arc->cost = 0; + #if( QUADRATICCOST ) + arc->quadraticCost = 0; + #endif + if (pU) + arc->upper = pU[ arc - arcsP ]; + else + arc->upper = Inf(); + arc->tail = nodesP + pSn[ arc - arcsP ] - 1; + arc->head = nodesP + pEn[ arc - arcsP ] - 1; + } + } + else { + stopNodesD = nodesD + n; + dummyRootD = nodesD + nmax; + for( nodeDType *node = nodesD ; node != stopNodesD ; node++ ) + node->balance = pDfct[ node - nodesD ]; // initialize nodes + + stopArcsD = arcsD + m; + dummyArcsD = arcsD + mmax; + stopDummyD = dummyArcsD + n; + for( arcDType *arc = arcsD ; arc != stopArcsD ; arc++ ) { + // initialize real arcs + arc->cost = pC[ arc - arcsD ]; + #if( QUADRATICCOST ) + arc->quadraticCost = 0; + #endif + arc->upper = pU[ arc - arcsD ]; + arc->tail = nodesD + pSn[ arc - arcsD ] - 1; + arc->head = nodesD + pEn[ arc - arcsD ] - 1; + } + + CreateAdditionalDualStructures(); + } + + if( pricingRule == kCandidateListPivot ) + MemAllocCandidateList(); + + status = kUnSolved; + } + } // end( MCFSimplex::LoadNet ) + +/*-------------------------------------------------------------------------*/ + +void MCFSimplex::SetAlg( bool UsPrml , char WhchPrc ) +{ + bool newUsePrimalSimplex = UsPrml; + bool oldUsePrimalSimplex = usePrimalSimplex; + char newPricingRule = WhchPrc; + char oldPricingRule = pricingRule; + + usePrimalSimplex = newUsePrimalSimplex; + pricingRule = newPricingRule; + + if( ( ! usePrimalSimplex ) && ( pricingRule == kDantzig) ) { + pricingRule = kFirstEligibleArc; + newPricingRule = pricingRule; + } + + if( ( newUsePrimalSimplex != oldUsePrimalSimplex ) || + ( newPricingRule != oldPricingRule ) ) { + MemDeAllocCandidateList(); + + if( newUsePrimalSimplex != oldUsePrimalSimplex ) { + #if( QUADRATICCOST ) + throw( + MCFException( "Primal Simplex is the only option if QUADRATICCOST == 1" + ) ); + } + #else + MemAlloc(); + nodePType *nP = nodesP; + nodeDType *nD = nodesD; + arcPType *aP = arcsP; + arcDType *aD = arcsD; + + if( newUsePrimalSimplex ) { // from Dual to Primal + if( nodesD == NULL ) + return; + + if( newSession ) + CreateInitialDualBase(); + + dummyRootP = nodesP + nmax; + stopNodesP = nodesP + n; + dummyArcsP = arcsP + mmax; + stopArcsP = arcsP + m; + stopDummyP = dummyArcsP + n; + // Copy the old Dual data structure in a new Primal data structure + while( nD != stopNodesD ) { + nP->prevInT = nodesP + ( nD->prevInT - nodesD ); + nP->nextInT = nodesP + ( nD->nextInT - nodesD ); + nP->enteringTArc = arcsP + ( nD->enteringTArc - arcsD ); + nP->balance = nD->balance; + nP->potential = nD->potential; + nP->subTreeLevel = nD->subTreeLevel; + nP++; + nD++; + } + + dummyRootP->prevInT = NULL; + dummyRootP->nextInT = nodesP + ( dummyRootD->nextInT - nodesD ); + dummyRootP->enteringTArc = arcsP + ( dummyRootD->enteringTArc - arcsD ); + dummyRootP->balance = dummyRootD->balance; + dummyRootP->potential = dummyRootD->potential; + dummyRootP->subTreeLevel = dummyRootD->subTreeLevel; + while( aD != stopArcsD ) { + aP->tail = nodesP + ( aD->tail - nodesD ); + aP->head = nodesP + ( aD->head - nodesD ); + aP->flow = aD->flow; + aP->cost = aD->cost; + aP->ident = aD->ident; + aP->upper = aD->upper; + aP++; + aD++; + } + + aP = dummyArcsP; + aD = dummyArcsD; + while( aD != stopDummyD ) { + aP->tail = nodesP + ( aD->tail - nodesD ); + aP->head = nodesP + ( aD->head - nodesD ); + aP->flow = aD->flow; + aP->cost = aD->cost; + aP->ident = aD->ident; + if( ( ETZ(aP->flow, EpsFlw) ) && (aP->ident == AT_UPPER) ) + aP->ident = AT_LOWER; + + aP->upper = Inf(); + aP++; + aD++; + } + + MemDeAlloc(false); + if( Senstv && ( status != kUnSolved ) ) { + nodePType *node = dummyRootP; + for( int i = 0 ; i < n ; i++ ) + node = node->nextInT; + + node->nextInT = NULL; + dummyRootP->prevInT = NULL; + dummyRootP->enteringTArc = NULL; + // balance the flow + CreateInitialPModifiedBalanceVector(); + PostPVisit( dummyRootP ); + // restore the primal admissibility + BalanceFlow( dummyRootP ); + ComputePotential( dummyRootP ); + } + else + status = kUnSolved; + } + else { // from Primal to Dual + if( nodesP == NULL ) + return; + + if( newSession ) + CreateInitialPrimalBase(); + + dummyRootD = nodesD + nmax; + stopNodesD = nodesD + n; + dummyArcsD = arcsD + mmax; + stopArcsD = arcsD + m; + stopDummyD = dummyArcsD + n; + // Copy the old Primal data structure in a new Dual data structure + while( nP != stopNodesP ) { + nD->prevInT = nodesD + ( nP->prevInT - nodesP ); + nD->nextInT = nodesD + ( nP->nextInT - nodesP ); + nD->enteringTArc = arcsD + ( nP->enteringTArc - arcsP ); + nD->balance = nP->balance; + nD->potential = nP->potential; + nD->subTreeLevel = nP->subTreeLevel; + nP++; + nD++; + } + + dummyRootD->prevInT = NULL; + dummyRootD->nextInT = nodesD + ( dummyRootP->nextInT - nodesP ); + dummyRootD->enteringTArc = NULL; + dummyRootD->balance = dummyRootP->balance; + dummyRootD->potential = dummyRootP->potential; + dummyRootD->subTreeLevel = dummyRootP->subTreeLevel; + while( aP != stopArcsP ) { + aD->tail = nodesD + ( aP->tail - nodesP ); + aD->head = nodesD + ( aP->head - nodesP ); + aD->flow = aP->flow; + aD->cost = aP->cost; + aD->ident = aP->ident; + aD->upper = aP->upper; + aP++; + aD++; + } + + aP = dummyArcsP; + aD = dummyArcsD; + while( aP != stopDummyP ) { + aD->tail = nodesD + ( aP->tail - nodesP ); + aD->head = nodesD + ( aP->head - nodesP ); + aD->flow = aP->flow; + aD->cost = aP->cost; + aD->ident = aP->ident; + aD->upper = 0; + aP++; + aD++; + } + + CreateAdditionalDualStructures(); + MemDeAlloc(true); + nodeDType *node = dummyRootD; + for( int i = 0 ; i < n ; i++ ) + node = node->nextInT; + + node->nextInT = NULL; + dummyRootD->enteringTArc = NULL; + dummyRootD->prevInT = NULL; + if( Senstv && ( status != kUnSolved ) ) { + // fix every flow arc according to its reduct cost + for( arcDType *arc = arcsD ; arc != stopArcsD ; arc++ ) { + if( (arc->ident == AT_LOWER) && LTZ( ReductCost( arc ) , EpsCst ) ) { + arc->flow = arc->upper; + arc->ident = AT_UPPER; + } + + if( ( arc->ident == AT_UPPER ) && GTZ( ReductCost( arc ) , EpsCst ) ) { + arc->flow = 0; + arc->ident = AT_LOWER; + } + } + + // balance the flow + CreateInitialDModifiedBalanceVector(); + PostDVisit( dummyRootD ); + } + else + status = kUnSolved; + } + //#endif + } +#endif + if( newPricingRule == kFirstEligibleArc ) + if( newUsePrimalSimplex ) + arcToStartP = arcsP; + else + arcToStartD = arcsD; + + if( ( nmax && mmax ) && ( newPricingRule == kCandidateListPivot ) ) + MemAllocCandidateList(); + } + } // end( SetAlg ) + +/*-------------------------------------------------------------------------*/ + +void MCFSimplex::SetPar( int par, int val ) +{ + switch( par ) { + case kAlgPrimal: + if( val == kYes ) + SetAlg( true , pricingRule); + + if( val == kNo ) + SetAlg( false, pricingRule ); + + break; + + case kAlgPricing: + + if( ( val == kDantzig ) || ( val == kFirstEligibleArc ) || + ( val == kCandidateListPivot ) ) + SetAlg( usePrimalSimplex , val ); + + break; + + case kNumCandList: + + MemDeAllocCandidateList(); + forcedNumCandidateList = val; + MemAllocCandidateList(); + forcedNumCandidateList = 0; + forcedHotListSize = 0; + break; + + case kHotListSize: + + MemDeAllocCandidateList(); + forcedHotListSize = val; + MemAllocCandidateList(); + forcedNumCandidateList = 0; + forcedHotListSize = 0; + break; + + case kRecomputeFOLimits: + + recomputeFOLimits = val; + break; + + default: + + MCFClass::SetPar(par, val); + } + } // end( SetPar( int ) ) + +/*-------------------------------------------------------------------------*/ + +void MCFSimplex::SetPar( int par , double val ) +{ + switch( par ) { + case kEpsOpt: + + EpsOpt = val; + break; + + default: + MCFClass::SetPar( par , val ); + } + } // end( SetPar( double ) + +/*-------------------------------------------------------------------------*/ +/*--------------- METHODS FOR SOLVING THE PROBLEM -------------------------*/ +/*-------------------------------------------------------------------------*/ + +void MCFSimplex::SolveMCF( void ) +{ + if( MCFt ) + MCFt->Start(); + + //if( status == kUnSolved ) + if(usePrimalSimplex ) + CreateInitialPrimalBase(); + else + CreateInitialDualBase(); + + newSession = false; + if( usePrimalSimplex ) + PrimalSimplex(); + else + DualSimplex(); + + if( MCFt ) + MCFt->Stop(); + + } // end( MCFSimplex::SolveMCF ) + +/*--------------------------------------------------------------------------*/ +/*---------------------- METHODS FOR READING RESULTS -----------------------*/ +/*--------------------------------------------------------------------------*/ + +void MCFSimplex::MCFGetX( FRow F , Index_Set nms , cIndex strt , Index stp ) +{ + if( stp > m ) + stp = m; + + if( nms ) { + if( usePrimalSimplex ) + for( Index i = strt ; i < stp ; i++ ) { + FNumber tXi = ( arcsP + i )->flow; + if( GTZ( tXi , EpsFlw ) ) { + *(F++) = tXi; + *(nms++) = i; + } + } + else + for( Index i = strt ; i < stp ; i++ ) { + FNumber tXi = ( arcsD + i )->flow; + if( GTZ( tXi , EpsFlw ) ) { + *(F++) = tXi; + *(nms++) = i; + } + } + + *nms = Inf(); + } + else + if( usePrimalSimplex ) + for( Index i = strt; i < stp; i++ ) + *(F++) = ( arcsP + i )->flow; + else + for( Index i = strt; i < stp; i++ ) + *(F++) = ( arcsD + i )->flow; + + } // end( MCFSimplex::MCFGetX( some ) ) + +/*--------------------------------------------------------------------------*/ + +void MCFSimplex::MCFGetRC( CRow CR , cIndex_Set nms , cIndex strt , Index stp ) +{ + if( nms ) { + while( *nms < strt ) + nms++; + + if( usePrimalSimplex ) + for( Index h ; ( h = *(nms++) ) < stp ; ) + *(CR++) = CNumber( ReductCost( arcsP + h ) ); + else + for( Index h ; ( h = *(nms++) ) < stp ; ) + *(CR++) = ReductCost( arcsD + h ); + } + else { + if( stp > m ) + stp = m; + + if( usePrimalSimplex ) + for( arcPType* arc = arcsP + strt ; arc < arcsP + stp ; arc++ ) + *(CR++) = CNumber( ReductCost( arc ) ); + else + for( arcDType* arc = arcsD + strt ; arc < arcsD + stp ; arc++ ) + *(CR++) = ReductCost( arc ); + } + } // end( MCFSimplex::MCFGetRC( some ) ) + +/*--------------------------------------------------------------------------*/ + +MCFSimplex::CNumber MCFSimplex::MCFGetRC( cIndex i ) +{ + if( usePrimalSimplex ) + return CNumber( ReductCost( arcsP + i ) ); + else + return( ReductCost( arcsD + i ) ); + + } // end( MCFSimplex::MCFGetRC( i ) ) + +/*--------------------------------------------------------------------------*/ + +void MCFSimplex::MCFGetPi( CRow P , cIndex_Set nms , cIndex strt , Index stp ) +{ + if( stp > n ) + stp = n; + + if( nms ) { + while( *nms < strt ) + nms++; + + if( usePrimalSimplex ) + for( Index h ; ( h = *(nms++) ) < stp ; ) + *(P++) = CNumber( (nodesP + h)->potential ); + else + for( Index h ; ( h = *(nms++) ) < stp ; ) + *(P++) = (nodesD + h )->potential; + } + else + if( usePrimalSimplex ) + for( nodePType *node = nodesP + strt ; node < ( nodesP + stp ) ; node++ ) + *(P++) = CNumber( node->potential ); + else + for( nodeDType *node = nodesD + strt ; node++ < ( nodesD + stp ) ; node++ ) + *(P++) = node->potential; + + } // end( MCFSimplex::MCFGetPi( some ) ) + +/*--------------------------------------------------------------------------*/ + +MCFSimplex::FONumber MCFSimplex::MCFGetFO( void ) +{ + if( status == kOK ) + return( (FONumber) GetFO() ); + else + if( status == kUnbounded ) + return( - Inf() ); + else + return( Inf() ); + + } // end( MCFSimplex::MCFGetFO ) + +/*-------------------------------------------------------------------------*/ +/*----------METHODS FOR READING THE DATA OF THE PROBLEM--------------------*/ +/*-------------------------------------------------------------------------*/ + +void MCFSimplex::MCFArcs( Index_Set Startv , Index_Set Endv , + cIndex_Set nms , cIndex strt , Index stp ) +{ + if( stp > m ) + stp = m; + + if( nms ) { + while( *nms < strt ) + nms++; + + if( usePrimalSimplex ) + for( Index h ; ( h = *(nms++) ) < stp ; ) { + if( Startv ) + *(Startv++) = Index( (arcsP + h)->tail - nodesP) - USENAME0; + if( Endv ) + *(Endv++) = Index( (arcsP + h)->head - nodesP ) - USENAME0; + } + else + for( Index h ; ( h = *(nms++) ) < stp ; ) { + if( Startv ) + *(Startv++) = Index( (arcsD + h)->tail - nodesD) - USENAME0; + if( Endv ) + *(Endv++) = Index( (arcsD + h)->head - nodesD ) - USENAME0; + } + } + else + if( usePrimalSimplex ) + for( arcPType* arc = arcsP + strt ; arc < (arcsP + stp) ; arc++ ) { + if( Startv ) + *(Startv++) = Index( arc->tail - nodesP ) - USENAME0; + if( Endv ) + *(Endv++) = Index( arc->head - nodesP ) - USENAME0; + } + else + for( arcDType* arc = arcsD + strt ; arc < (arcsD + stp) ; arc++ ) { + if( Startv ) + *(Startv++) = Index( arc->tail - nodesD ) - USENAME0; + if( Endv ) + *(Endv++) = Index( arc->head - nodesD ) - USENAME0; + } + + } // end( MCFSimplex::MCFArcs ) + +/*-------------------------------------------------------------------------*/ + +void MCFSimplex::MCFCosts( CRow Costv , cIndex_Set nms , + cIndex strt , Index stp ) +{ + if( stp > m ) + stp = m; + + if( nms ) { + while( *nms < strt ) + nms++; + + if( usePrimalSimplex ) + for( Index h ; ( h = *(nms++) ) < stp ; ) + *(Costv++) = (arcsP + h)->cost; + else + for( Index h ; ( h = *(nms++) ) < stp ; ) + *(Costv++) = (arcsD + h)->cost; + } + else + if( usePrimalSimplex ) + for( arcPType* arc = arcsP + strt ; arc < (arcsP + stp) ; arc++ ) + *(Costv++) = arc->cost; + else + for( arcDType* arc = arcsD + strt ; arc < (arcsD + stp) ; arc++ ) + *(Costv++) = arc->cost; + + } // end( MCFSimplex::MCFCosts ( some ) ) + +/*-------------------------------------------------------------------------*/ + +void MCFSimplex::MCFQCoef( CRow Qv , cIndex_Set nms , + cIndex strt , Index stp ) +{ + if( stp > m ) + stp = m; + + if( nms ) { + while( *nms < strt ) + nms++; + + #if( QUADRATICCOST ) + if( usePrimalSimplex ) + for( Index h ; ( h = *(nms++) ) < stp ; ) + *(Qv++) = (arcsP + h)->quadraticCost; + else + for( Index h ; ( h = *(nms++) ) < stp ; ) + *(Qv++) = (arcsD + h)->quadraticCost; + #else + for( Index h ; ( h = *(nms++) ) < stp ; ) + *(Qv++) = 0; + #endif + } + else + #if( QUADRATICCOST ) + if( usePrimalSimplex ) + for( arcPType* arc = arcsP + strt ; arc < ( arcsP + stp ) ; arc++ ) + *(Qv++) = arc->quadraticCost; + else + for( arcDType* arc = arcsD + strt ; arc < ( arcsD + stp ) ; arc++ ) + *(Qv++) = arc->quadraticCost; + #else + for( Index h = strt ; h++ < stp ; ) + *(Qv++) = 0; + #endif + + } // end( MCFSimplex::MCFQCoef ( some ) ) + +/*-------------------------------------------------------------------------*/ + +void MCFSimplex::MCFUCaps( FRow UCapv , cIndex_Set nms , + cIndex strt , Index stp ) +{ + if( stp > m ) + stp = m; + + if( nms ) { + while( *nms < strt ) + nms++; + + if( usePrimalSimplex ) + for( Index h ; ( h = *(nms++) ) < stp ; ) + *(UCapv++) = (arcsP + h)->upper; + else + for( Index h ; ( h = *(nms++) ) < stp ; ) + *(UCapv++) = (arcsD + h)->upper; + } + else + if( usePrimalSimplex ) + for( arcPType* arc = arcsP + strt ; arc < (arcsP + stp ) ; arc++ ) + *(UCapv++) = arc->upper; + else + for( arcDType* arc = arcsD + strt ; arc < ( arcsD + stp ) ; arc++ ) + *(UCapv++) = arc->upper; + + } // end( MCFSimplex::MCFUCaps ( some ) ) + +/*-------------------------------------------------------------------------*/ + +void MCFSimplex::MCFDfcts( FRow Dfctv , cIndex_Set nms , + cIndex strt , Index stp ) +{ + if( stp > n ) + stp = n; + + if( nms ) { + while( *nms < strt ) + nms++; + + if( usePrimalSimplex ) + for( Index h ; ( h = *(nms++) ) < stp ; ) + *(Dfctv++) = ( nodesP + h )->balance; + else + for( Index h ; ( h = *(nms++) ) < stp ; ) + *(Dfctv++) = (nodesD + h )->balance; + } + else + if( usePrimalSimplex ) + for( nodePType* node = nodesP + strt ; node < ( nodesP + stp ) ; node++ ) + *(Dfctv++) = node->balance; + else + for( nodeDType* node = nodesD + strt ; node < ( nodesD + stp ) ; node++ ) + *(Dfctv++) = node->balance; + + } // end( MCFSimplex::MCFDfcts ) + +/*-------------------------------------------------------------------------*/ +/*--------- METHODS FOR ADDING / REMOVING / CHANGING DATA -----------------*/ +/*-------------------------------------------------------------------------*/ + +void MCFSimplex::ChgCosts( cCRow NCost , cIndex_Set nms , + cIndex strt , Index stp ) +{ + if( stp > m ) + stp = m; + + if( nms ) { + while( *nms < strt ) { + nms++; + NCost++; + } + + cIndex_Set tnms = nms; // nms may be needed below + if( usePrimalSimplex ) + for( Index h ; ( h = *(tnms++) ) < stp ; ) + arcsP[ h ].cost = *(NCost++); + else + for( Index h ; ( h = *(tnms++) ) < stp ; ) + arcsD[ h ].cost = *(NCost++); + } + else + if( usePrimalSimplex ) + for( arcPType *arc = arcsP + strt ; arc < (arcsP + stp) ; arc++ ) + arc->cost = *(NCost++); + else + for( arcDType *arc = arcsD + strt ; arc < (arcsD + stp) ; arc++ ) + arc->cost = *(NCost++); + + if( Senstv && ( status != kUnSolved ) ) + if( usePrimalSimplex ) + ComputePotential( dummyRootP ); + else { + #if( QUADRATICCOST == 0 ) + for( arcDType *arc = arcsD ; arc != stopArcsD ; arc++ ) + if( arc->ident > BASIC ) + if( GTZ( ReductCost( arc ) , EpsCst ) ) { + arc->flow = 0; + arc->ident = AT_LOWER; + } + else { + arc->flow = arc->upper; + arc->ident = AT_UPPER; + } + + CreateInitialDModifiedBalanceVector(); + PostDVisit( dummyRootD ); + #endif + } + else + status = kUnSolved; + + } // end( MCFSimplex::ChgCosts ) + +/*-------------------------------------------------------------------------*/ + +void MCFSimplex::ChgCost( Index arc , cCNumber NCost ) +{ + if( arc >= m ) + return; + + if( usePrimalSimplex ) + ( arcsP + arc )->cost = NCost; + else + ( arcsD + arc )->cost = NCost; + + if( Senstv && ( status != kUnSolved ) ) { + if( usePrimalSimplex ) { + nodePType *node = ( arcsP + arc )->tail; + if( ( ( arcsP + arc )->head)->subTreeLevel < node->subTreeLevel ) + node = ( arcsP + arc )->head; + + ComputePotential( dummyRootP ); + } + else { + #if( QUADRATICCOST == 0 ) + nodeDType *node = ( arcsD + arc )->tail; + if( ( ( arcsD + arc )->head )->subTreeLevel < node->subTreeLevel ) + node = ( arcsD + arc )->head; + + ComputePotential( dummyRootD ); + for( arcDType *a = arcsD ; a != stopArcsD ; a++) + if( a->ident > BASIC ) + if( GTZ( ReductCost( a ) , EpsCst ) ) { + a->flow = 0; + a->ident = AT_LOWER; + } + else { + a->flow = a->upper; + a->ident = AT_UPPER; + } + + CreateInitialDModifiedBalanceVector(); + PostDVisit( dummyRootD ); + #endif + } + } + else + status = kUnSolved; + + } // end( MCFSimplex::ChgCost ) + +/*-------------------------------------------------------------------------*/ + +void MCFSimplex::ChgQCoef( cCRow NQCoef , cIndex_Set nms , + cIndex strt , Index stp ) +{ + if( stp > m ) + stp = m; + + #if( QUADRATICCOST ) + if( nms ) { + while( *nms < strt ) { + nms++; + NQCoef++; + } + + cIndex_Set tnms = nms; // nms may be needed below + if( usePrimalSimplex ) + for( Index h ; ( h = *(tnms++) ) < stp ; ) + arcsP[ h ].quadraticCost = *(NQCoef++); + else + for( Index h ; ( h = *(tnms++) ) < stp ; ) + arcsD[ h ].quadraticCost = *(NQCoef++); + } + else + if( usePrimalSimplex ) + for( arcPType *arc = arcsP + strt ; arc < ( arcsP + stp ) ; arc++ ) + arc->quadraticCost = *(NQCoef++); + else + for( arcDType *arc = arcsD + strt ; arc < ( arcsD + stp ) ; arc++ ) + arc->quadraticCost = *(NQCoef++); + + if( Senstv && (status != kUnSolved ) ) + ComputePotential( dummyRootP ); + else + status = kUnSolved; + #else + if( NQCoef ) + throw( MCFException( "ChgQCoef: nonzero coefficients not allowed" ) ); + #endif +} // end( MCFSimplex::ChgQCoef ) + +/*-------------------------------------------------------------------------*/ + +void MCFSimplex::ChgQCoef( Index arc , cCNumber NQCoef ) +{ + #if( QUADRATICCOST ) + if( arc >= m ) + return; + + if( usePrimalSimplex ) + ( arcsP + arc )->quadraticCost = NQCoef; + else + ( arcsD + arc )->quadraticCost = NQCoef; + + if( Senstv && ( status != kUnSolved ) ) { + nodePType *node = ( arcsP + arc )->tail; + if( ( ( arcsP + arc )->head )->subTreeLevel < node->subTreeLevel ) + node = ( arcsP + arc )->head; + + ComputePotential( node ); + } + else + status = kUnSolved; + #else + if( NQCoef ) + throw( MCFException( "ChgQCoef: nonzero coefficients not allowed" ) ); + #endif + } // end( MCFSimplex::ChgQCoef ) + +/*-------------------------------------------------------------------------*/ + +void MCFSimplex::ChgDfcts( cFRow NDfct , cIndex_Set nms , + cIndex strt , Index stp ) +{ + if( stp > m ) + stp = m; + + if( nms ) { + while( *nms < strt ) { + nms++; + NDfct++; + } + + cIndex_Set tnms = nms; // nms may be needed below + if( usePrimalSimplex ) + for( Index h ; ( h = *(tnms++) ) < stp ; ) + nodesP[ h ].balance = *(NDfct++); + else + for( Index h ; ( h = *(tnms++) ) < stp ; ) + nodesD[ h ].balance = *(NDfct++); + } + else + if( usePrimalSimplex ) + for( nodePType *node = nodesP + strt ; node < ( nodesP + stp ) ; node++ ) + node->balance = *(NDfct++); + else + for( nodeDType *node = nodesD + strt ; node < ( nodesD + stp ) ; node++ ) + node->balance = *(NDfct++); + + if( Senstv && (status != kUnSolved ) ) + if( usePrimalSimplex ) { + CreateInitialPModifiedBalanceVector(); + PostPVisit( dummyRootP ); + BalanceFlow( dummyRootP ); + ComputePotential( dummyRootP ); + } + else { + CreateInitialDModifiedBalanceVector(); + PostDVisit( dummyRootD ); + } + else + status = kUnSolved; + + } // end( MCFSimplex::ChgDfcts ) + +/*-------------------------------------------------------------------------*/ + +void MCFSimplex::ChgDfct( Index nod , cFNumber NDfct ) +{ + if( nod > n ) + return; + + if( usePrimalSimplex ) + ( nodesP + nod - 1 )->balance = NDfct; + else + ( nodesD + nod - 1 )->balance = NDfct; + + if( Senstv && (status != kUnSolved ) ) + if( usePrimalSimplex ) { + CreateInitialPModifiedBalanceVector(); + PostPVisit( dummyRootP ); + BalanceFlow( dummyRootP ); + ComputePotential( dummyRootP ); + } + else { + CreateInitialDModifiedBalanceVector(); + PostDVisit( dummyRootD ); + } + else + status = kUnSolved; + + } // end( MCFSimplex::ChgDfct ) + +/*-------------------------------------------------------------------------*/ + +void MCFSimplex::ChgUCaps( cFRow NCap , cIndex_Set nms , + cIndex strt , Index stp ) +{ + if( stp > m ) + stp = m; + + if( nms ) { + while( *nms < strt ) { + nms++; + NCap++; + } + + cIndex_Set tnms = nms; // nms may be needed below + if( usePrimalSimplex ) + for( Index h ; ( h = *(tnms++) ) < stp ; ) + arcsP[ h ].upper = *(NCap++); + else + for( Index h ; ( h = *(tnms++) ) < stp ; ) + arcsD[ h ].upper = *(NCap++); + } + else + if( usePrimalSimplex ) + for( arcPType *arc = arcsP + strt ; arc < ( arcsP + stp ) ; arc++ ) + arc->upper = *(NCap++); + else + for( arcDType *arc = arcsD + strt ; arc < ( arcsD + stp ) ; arc++ ) + arc->upper = *(NCap++); + + if( Senstv && (status != kUnSolved ) ) { + if( usePrimalSimplex ) { + for( arcPType *arc = arcsP ; arc != stopArcsP ; arc++) + #if( QUADRATICCOST ) + if( GT( arc->flow , arc->upper , EpsFlw ) ) + arc->flow = arc->upper; + #else + if( GT(arc->flow , arc->upper , EpsFlw ) || + ( ( arc->ident == AT_UPPER ) && + ( ! ETZ( arc->flow - arc->upper , EpsFlw ) ) ) ) + arc->flow = arc->upper; + #endif + + CreateInitialPModifiedBalanceVector(); + PostPVisit( dummyRootP ); + BalanceFlow( dummyRootP ); + ComputePotential( dummyRootP ); + } + else { + #if( QUADRATICCOST == 0 ) + for( arcDType *arc = arcsD ; arc != stopArcsD ; arc++ ) + if( ( GT( arc->flow , arc->upper , EpsFlw ) && ( arc->ident != BASIC ) ) || + ( ( arc->ident == AT_UPPER ) && + ( ! ETZ( arc->flow - arc->upper , EpsFlw ) ) ) ) + arc->flow = arc->upper; + + CreateInitialDModifiedBalanceVector(); + PostDVisit( dummyRootD ); + ComputePotential( dummyRootD ); + #endif + } + } + else + status = kUnSolved; + + } // end( MCFSimplex::ChgUCaps ) + +/*-------------------------------------------------------------------------*/ + +void MCFSimplex::ChgUCap( Index arc , cFNumber NCap ) +{ + if( arc >= m ) + return; + + if( usePrimalSimplex ) + ( arcsP + arc )->upper = NCap; + else + ( arcsD + arc )->upper = NCap; + + if( Senstv && (status != kUnSolved ) ) { + if( usePrimalSimplex ) { + #if( QUADRATICCOST ) + if( GT( ( arcsP + arc )->flow , ( arcsP + arc )->upper , EpsFlw ) ) + ( arcsP + arc )->flow = ( arcsP + arc )->upper; + #else + if( GT( ( arcsP + arc )->flow , ( arcsP + arc )->upper , EpsFlw ) || + ( ( ( arcsP + arc )->ident == AT_UPPER ) && + ( ! ETZ( ( arcsP + arc )->flow - ( arcsP + arc )->upper , EpsFlw ) ) ) ) + ( arcsP + arc )->flow = ( arcsP + arc )->upper; + #endif + + CreateInitialPModifiedBalanceVector(); + PostPVisit( dummyRootP ); + BalanceFlow( dummyRootP ); + ComputePotential( dummyRootP ); + } + else { + #if( QUADRATICCOST == 0 ) + if( ( GT( ( arcsD + arc )->flow , ( arcsD + arc )->upper , EpsFlw ) && + ( ( ( arcsD + arc )->ident != BASIC ) ) ) || + ( ( ( arcsD + arc )->ident == AT_UPPER ) && + ( ! ETZ( ( arcsD + arc )->flow - ( arcsD + arc )->upper , EpsFlw ) ) ) ) { + ( arcsD + arc )->flow = ( arcsD + arc )->upper; + ( arcsD + arc )->ident = AT_UPPER; + } + + CreateInitialDModifiedBalanceVector(); + PostDVisit( dummyRootD ); + ComputePotential( dummyRootD ); + #endif + } + } + else + status = kUnSolved; + + } // end( MCFSimplex::ChgUCap ) + +/*-------------------------------------------------------------------------*/ + +bool MCFSimplex::IsClosedArc( cIndex name ) +{ + if( name >= m ) + return( false ); + + #if( QUADRATICCOST ) + return( ( arcsP + name )->cost == Inf() ); + #else + if( usePrimalSimplex ) + return( ( ( arcsP + name )->ident < BASIC) ); + else + return( ( ( arcsD + name )->ident < BASIC) ); + #endif + } + +/*-------------------------------------------------------------------------*/ + +void MCFSimplex::CloseArc( cIndex name ) +{ + if( name >= m ) + return; + + if( usePrimalSimplex ) { + arcPType *arc = arcsP + name; + #if( QUADRATICCOST ) + if( arc->cost == Inf() ) + return; + + arc->cost = Inf(); + #else + if( arc->ident < BASIC ) + return; + + arc->ident = CLOSED; + #endif + + arc->flow = 0; + + if( Senstv && ( status != kUnSolved ) ) { + nodePType *node = NULL; + if( (arc->tail)->enteringTArc == arc ) + node = arc->tail; + + if( (arc->head)->enteringTArc == arc ) + node = arc->head; + + if( node ) { + node->enteringTArc = dummyArcsP + ( node - nodesP ); + nodePType *last = CutAndUpdateSubtree( node , -node->subTreeLevel + 1 ); + PasteSubtree( node , last , dummyRootP ); + node->enteringTArc = dummyArcsP + ( node - nodesP ); + } + + CreateInitialPModifiedBalanceVector(); + PostPVisit(dummyRootP); + BalanceFlow(dummyRootP); + ComputePotential(dummyRootP); + } + else + status = kUnSolved; + } + else { + #if( QUADRATICCOST == 0 ) + arcDType *arc = arcsD + name; + if( arc->ident < BASIC ) + return; + + arc->flow = 0; + arc->ident = CLOSED; + + if( Senstv && ( status != kUnSolved ) ) { + nodeDType *node = NULL; + if( ( arc->tail )->enteringTArc == arc) + node = arc->tail; + + if( ( arc->head )->enteringTArc == arc ) + node = arc->head; + + if( node ) { + node->enteringTArc = dummyArcsD + ( node - nodesD ); + nodeDType *last = CutAndUpdateSubtree( node , -node->subTreeLevel + 1 ); + PasteSubtree( node , last , dummyRootD ); + node->enteringTArc = dummyArcsD + ( node - nodesD ); + ComputePotential( dummyRootD ); + + for( arcDType *a = arcsD ; a != stopArcsD ; a++ ) + if( a->ident > BASIC ) + if( GTZ( ReductCost( a ) , EpsCst ) ) { + a->flow = 0; + a->ident = AT_LOWER; + } + else { + a->flow = a->upper; + a->ident = AT_UPPER; + } + } + + CreateInitialDModifiedBalanceVector(); + PostDVisit(dummyRootD); + ComputePotential(dummyRootD); + } + else + status = kUnSolved; + #endif + } + } // end( MCFSimplex::CloseArc ) + +/*--------------------------------------------------------------------------*/ + +void MCFSimplex::DelNode( cIndex name ) +{ + if( name >= n ) + return; + + if( usePrimalSimplex ) { + nodePType *node = nodesP + name; + nodePType *last = CutAndUpdateSubtree(node, -node->subTreeLevel); + nodePType *n = node->nextInT; + while( n ) { + if( n->subTreeLevel == 1 ) + n->enteringTArc = dummyArcsP + ( n - nodesP ); + + n = n->nextInT; + } + + PasteSubtree( node , last , dummyRootP ); + n = node->nextInT; + dummyRootP->nextInT = n; + n->prevInT = dummyRootP; + + for( arcPType *arc = arcsP ; arc != stopArcsP ; arc++ ) { + if( ( ( arc->tail ) == node) || ( ( arc->head ) == node ) ) { + arc->flow = 0; + #if( QUADRATICCOST ) + arc->cost = Inf(); + #else + arc->ident = CLOSED; + #endif + } + } + + for( arcPType *arc = dummyArcsP ; arc != stopDummyP ; arc++ ) { + if( ( ( arc->tail ) == node ) || ( ( arc->head ) == node ) ) { + arc->flow = 0; + #if( QUADRATICCOST ) + arc->cost = Inf(); + #else + arc->ident = CLOSED; + #endif + } + } + + CreateInitialPModifiedBalanceVector(); + PostPVisit( dummyRootP ); + BalanceFlow( dummyRootP ); + ComputePotential( dummyRootP ); + } + else { + #if( QUADRATICCOST == 0 ) + nodeDType *node = nodesD + name; + nodeDType *last = CutAndUpdateSubtree( node , -node->subTreeLevel ); + nodeDType *n = node->nextInT; + while( n ) { + if( n->subTreeLevel == 1 ) + n->enteringTArc = dummyArcsD + ( n - nodesD ); + + n = n->nextInT; + } + + PasteSubtree( node , last , dummyRootD ); + n = node->nextInT; + dummyRootD->nextInT = n; + n->prevInT = dummyRootD; + + for( arcDType *arc = arcsD ; arc != stopArcsD ; arc++ ) + if( ( ( arc->tail ) == node) || ( ( arc->head ) == node ) ) { + arc->flow = 0; + arc->ident = CLOSED; + } + + for( arcDType *arc = dummyArcsD ; arc != stopDummyD ; arc++ ) + if( ( ( arc->tail ) == node ) || ( ( arc->head ) == node ) ) { + arc->flow = 0; + arc->ident = CLOSED; + } + + CreateInitialDModifiedBalanceVector(); + PostDVisit( dummyRootD ); + ComputePotential( dummyRootD ); + #endif + } + } // end( MCFSimplex::DelNode ) + +/*--------------------------------------------------------------------------*/ + +void MCFSimplex::OpenArc( cIndex name ) +{ + if( name >= m ) + return; + + if( usePrimalSimplex ) { + /* Quadratic case is not implemented for a theory bug. + Infact a closed arc in the quadratic case has its cost fixed to infinity, and + it's impossible to restore the old value. */ + + #if( QUADRATICCOST == 0 ) + arcPType *arc = arcsP + name; + if( arc->ident == CLOSED ) { + arc->ident = AT_LOWER; + arc->flow = 0; + } + #endif + } + else { + #if( QUADRATICCOST == 0 ) + arcDType *arc = arcsD + name; + if( arc->ident == CLOSED ) { + if( GTZ( ReductCost( arc ) , EpsCst ) ) + arc->ident = AT_LOWER; + else { + arc->ident = AT_UPPER; + arc->flow = arc->upper; + if( Senstv && ( status != kUnSolved ) ) { + CreateInitialDModifiedBalanceVector(); + PostDVisit( dummyRootD ); + } + else + status = kUnSolved; + } + } + #endif + } + } // end( MCFSimplex:OpenArc ) + +/*--------------------------------------------------------------------------*/ + +MCFSimplex::Index MCFSimplex::AddNode( cFNumber aDfct ) +{ + if( n >= nmax ) + return( Inf() ); + + n++; + if( usePrimalSimplex ) { + nodePType *newNode = nodesP + n - 1; + stopArcsP->tail = newNode; + stopArcsP->head = dummyRootP; + stopArcsP->upper = Inf(); + stopArcsP->flow = 0; + stopArcsP->cost = Inf(); + #if( QUADRATICCOST ) + stopArcsP->quadraticCost = 0; + #else + stopArcsP->ident = BASIC; + #endif + stopArcsP++; + newNode->balance = aDfct; + newNode->prevInT = dummyRootP; + newNode->nextInT = dummyRootP->nextInT; + (dummyRootP->nextInT)->prevInT = newNode; + dummyRootP->nextInT = newNode; + newNode->enteringTArc = stopArcsP--; + newNode->potential = 0; + #if(QUADRATICCOST) + newNode->sumQuadratic = 0; + #endif + } + else { + #if( QUADRATICCOST == 0 ) + nodeDType *newNode = nodesD + n - 1; + stopArcsD->tail = newNode; + stopArcsD->head = dummyRootD; + stopArcsD->upper = 0; + stopArcsD->flow = 0; + stopArcsD->cost = Inf(); + stopArcsD->ident = BASIC; + newNode->balance = aDfct; + newNode->prevInT = dummyRootD; + newNode->nextInT = dummyRootD->nextInT; + (dummyRootD->nextInT)->prevInT = newNode; + dummyRootD->nextInT = newNode; + newNode->enteringTArc = stopArcsD; + newNode->potential = 0; + newNode->firstFs = stopArcsD; + newNode->firstBs = NULL; + stopArcsD->nextFs = NULL; + stopArcsD->nextBs = dummyRootD->firstBs; + dummyRootD->firstBs = stopArcsD; + stopArcsD++; + #endif + } + + return( n ); + + } // end( MCFSimplex::AddNode ) + +/*--------------------------------------------------------------------------*/ + +void MCFSimplex::ChangeArc( cIndex name , cIndex nSN , cIndex nEN ) +{ + if( name >= m ) + return; + + CloseArc( name ); + + if( usePrimalSimplex ) { + if( nSN <= n ) + (arcsP + name)->tail = (nodesP + nSN + USENAME0 - 1); + if( nEN <= n ) + (arcsP + name)->head = (nodesP + nEN + USENAME0 - 1); + } + else { + if( nSN <= n ) + (arcsD + name)->tail = (nodesD + nSN + USENAME0 - 1); + if( nEN <= n ) + (arcsD + name)->head = (nodesD + nEN + USENAME0 - 1); + } + + OpenArc( name ); + + } // end( MCFSimplex::ChangeArc ) + +/*--------------------------------------------------------------------------*/ + +void MCFSimplex::DelArc( cIndex name ) +{ + if( name >= m ) + return; + + if( usePrimalSimplex ) { + arcPType *arc = arcsP + name; + #if( QUADRATICCOST ) + if( arc->upper == -Inf() ) + return; + + if( arc->cost < Inf() ) + #else + if( arc->cost == DELETED ) + return; + + if( arc->cost >= BASIC ) + #endif + CloseArc( name ); + + #if( QUADRATICCOST ) + arc->upper = -Inf(); + #else + arc->ident = DELETED; + #endif + } + else { + #if( QUADRATICCOST == 0 ) + arcDType *arc = arcsD + name; + if( arc->cost == DELETED ) + return; + + if( arc->cost >= BASIC ) + CloseArc( name ); + + arc->ident = DELETED; + #endif + } + } // end( MCFSimplex::DelArc ) + +/*--------------------------------------------------------------------------*/ + +MCFSimplex::Index MCFSimplex::AddArc( cIndex Start , cIndex End , + cFNumber aU , cCNumber aC ) +{ + if( usePrimalSimplex ) { + arcPType *arc = arcsP; + #if( QUADRATICCOST ) + while( ( arc->upper != -Inf() ) && ( arc != stopArcsP ) ) + #else + while( ( arc->ident > DELETED ) && ( arc != stopArcsP ) ) + #endif + arc++; + + if( arc == stopArcsP ) { + if( m >= mmax ) + return( Inf() ); + + m++; + stopArcsP++; + } + + Index pos = ( arc - arcsP ) + 1; + arc->tail = nodesP + Start + USENAME0 - 1; + arc->head = nodesP + End + USENAME0 - 1; + arc->upper = aU; + arc->cost = aC; + arc->flow = 0; + #if( QUADRATICCOST ) + arc->quadraticCost = 0; + #else + arc->ident = AT_LOWER; + #endif + ComputePotential( dummyRootP ); + return( pos ); + } + else { + #if( QUADRATICCOST == 0 ) + arcDType *arc = arcsD; + while( ( arc->ident > DELETED ) && ( arc != stopArcsD ) ) + arc++; + + if( arc == stopArcsD ) { + if( m >= mmax ) + return( Inf() ); + + m++; + stopArcsD++; + } + + Index pos = ( arc - arcsD ) + 1; + arc->tail = nodesD + Start + USENAME0 - 1; + arc->head = nodesD + End + USENAME0 - 1; + arc->upper = aU; + arc->cost = aC; + if( GEZ( ReductCost( arc ) , EpsCst ) ) { + arc->flow = 0; + arc->ident = AT_LOWER; + if( Senstv && ( status != kUnSolved ) ) { + CreateInitialDModifiedBalanceVector(); + PostDVisit( dummyRootD ); + ComputePotential( dummyRootD ); + } + else + status = kUnSolved; + } + else { + arc->flow = arc->upper; + arc->ident = AT_UPPER; + if( Senstv && ( status != kUnSolved ) ) { + CreateInitialDModifiedBalanceVector(); + PostDVisit( dummyRootD ); + ComputePotential( dummyRootD ); + } + else + status = kUnSolved; + } + + ComputePotential( dummyRootD ); + return( pos ); + #endif + } + } // end( MCFSimplex::AddArc ) + +/*--------------------------------------------------------------------------*/ + +bool MCFSimplex::IsDeletedArc( cIndex name ) +{ + if( name >= m ) + return( false ); + + #if( QUADRATICCOST ) + return( ( ( arcsP + name )->upper == -Inf() ) ); + #else + if( usePrimalSimplex ) + return( ( arcsP + name )->ident == DELETED ); + else + return( ( arcsD + name )->ident == DELETED ); + #endif + } + +/*--------------------------------------------------------------------------*/ +/*------------------------------ DESTRUCTOR --------------------------------*/ +/*--------------------------------------------------------------------------*/ + +MCFSimplex::~MCFSimplex() +{ + MemDeAllocCandidateList(); + MemDeAlloc(true); + MemDeAlloc(false); + } + +/*--------------------------------------------------------------------------*/ +/*---------------------------- PRIVATE METHODS -----------------------------*/ +/*--------------------------------------------------------------------------*/ + +void MCFSimplex::MemAlloc( void ) +{ + if( usePrimalSimplex ) { + nodesP = new nodePType[ nmax + 1 ]; // array of nodes + arcsP = new arcPType[ mmax + nmax ]; // array of arcs + dummyArcsP = arcsP + mmax; // artificial arcs are in the last + // nmax positions of the array arcs[] + } + else { + nodesD = new nodeDType[ nmax + 1 ]; // array of nodes + arcsD = new arcDType[ mmax + nmax ]; // array of arcs + dummyArcsD = arcsD + mmax; // artificial arcs are in the last nmax + // positions of the array arcs[] + } + } + +/*--------------------------------------------------------------------------*/ + +void MCFSimplex::MemDeAlloc( bool whatDeAlloc ) +{ + if( whatDeAlloc ) { + delete[] nodesP; + delete[] arcsP; + nodesP = NULL; + arcsP = NULL; + } + else { + delete[] nodesD; + delete[] arcsD; + nodesD = NULL; + arcsD = NULL; + } + MemDeAllocCandidateList( ); + } + +/*--------------------------------------------------------------------------*/ + +void MCFSimplex::MemAllocCandidateList( void ) +{ + if( usePrimalSimplex ) { + if( m < 10000 ) { + numCandidateList = PRIMAL_LOW_NUM_CANDIDATE_LIST; + hotListSize = PRIMAL_LOW_HOT_LIST_SIZE; + } + else + if( m > 100000 ) { + numCandidateList = PRIMAL_HIGH_NUM_CANDIDATE_LIST; + hotListSize = PRIMAL_HIGH_HOT_LIST_SIZE ; + } + else { + numCandidateList = PRIMAL_MEDIUM_NUM_CANDIDATE_LIST; + hotListSize = PRIMAL_MEDIUM_HOT_LIST_SIZE; + } + + #if( QUADRATICCOST ) + int coef = 1; + // If the number of the arcs is more than 10000, numCandidateList and hotListSize + // are increased to improve the performance of the Quadratic Primal Simplex + if( m > 10000 ) + coef = 10; + + numCandidateList = numCandidateList * coef; + hotListSize = hotListSize * coef; + #endif + + if( forcedNumCandidateList > 0 ) + numCandidateList = forcedNumCandidateList; + + if( forcedHotListSize > 0 ) + hotListSize = forcedHotListSize; + + candP = new primalCandidType[ hotListSize + numCandidateList + 1 ]; + } + else { + if( n < 10000 ) { + numCandidateList = DUAL_LOW_NUM_CANDIDATE_LIST; + hotListSize = DUAL_LOW_HOT_LIST_SIZE; + } + else { + numCandidateList = DUAL_HIGH_NUM_CANDIDATE_LIST; + hotListSize = DUAL_HIGH_HOT_LIST_SIZE; + } + + if( forcedNumCandidateList > 0 ) + numCandidateList = forcedNumCandidateList; + + if( forcedHotListSize > 0 ) + hotListSize = forcedHotListSize; + + candD = new dualCandidType[ hotListSize + numCandidateList + 1 ]; + } + } + +/*--------------------------------------------------------------------------*/ + +void MCFSimplex::MemDeAllocCandidateList( void ) +{ + delete[] candP; + candP = NULL; + delete[] candD; + candD = NULL; +} + +/*--------------------------------------------------------------------------*/ + +void MCFSimplex::CreateInitialPrimalBase( void ) +{ + arcPType *arc; + nodePType *node; + for( arc = arcsP ; arc != stopArcsP ; arc++ ) { // initialize real arcs + arc->flow = 0; + #if( QUADRATICCOST == 0 ) + arc->ident = AT_LOWER; + #endif + } + + for( arc = dummyArcsP ; arc != stopDummyP ; arc++ ) { // initialize dummy arcs + node = nodesP + ( arc - dummyArcsP ); + if( node->balance > 0 ) { // sink nodes + arc->tail = dummyRootP; + arc->head = node; + arc->flow = node->balance; + } + else { // source nodes or transit node + arc->tail = node; + arc->head = dummyRootP; + arc->flow = -node->balance; + } + + arc->cost = MAX_ART_COST; + #if( QUADRATICCOST ) + arc->quadraticCost = 0; + #else + arc->ident = BASIC; + #endif + arc->upper = Inf(); + } + + dummyRootP->balance = 0; + dummyRootP->prevInT = NULL; + dummyRootP->nextInT = nodesP; + dummyRootP->enteringTArc = NULL; + #if( QUADRATICCOST ) + dummyRootP->sumQuadratic = 0; + #endif + dummyRootP->potential = MAX_ART_COST; + dummyRootP->subTreeLevel = 0; + for( node = nodesP ; node != stopNodesP ; node++) { // initialize nodes + node->prevInT = node - 1; + node->nextInT = node + 1; + node->enteringTArc = dummyArcsP + (node - nodesP); + #if( QUADRATICCOST ) + node->sumQuadratic = (node->enteringTArc)->quadraticCost; + #endif + if( node->balance > 0 ) // sink nodes + node->potential = 2 * MAX_ART_COST; + else // source nodes or transit node + node->potential = 0; + + node->subTreeLevel = 1; + } + + nodesP->prevInT = dummyRootP; + ( nodesP + n - 1 )->nextInT = NULL; + } + +/*--------------------------------------------------------------------------*/ + +void MCFSimplex::CreateInitialDualBase( void ) +{ + arcDType *arc; + nodeDType *node; + for( arc = dummyArcsD ; arc != stopDummyD ; arc++ ) { // initialize dummy arcs + node = nodesD + ( arc - dummyArcsD ); + arc->tail = node; + arc->head = dummyRootD; + arc->flow = -node->balance; + arc->cost = MAX_ART_COST; + #if( QUADRATICCOST ) + arc->quadraticCost = 0; + #else + arc->ident = BASIC; + #endif + arc->upper = 0; + } + + for( arc = arcsD ; arc != stopArcsD ; arc++ ) { // initialize real arcs + if( GTZ( arc->cost , EpsCst ) ) { + arc->flow = 0; + #if( QUADRATICCOST == 0 ) + arc->ident = AT_LOWER; + #endif + } + else { + #if( QUADRATICCOST == 0 ) + arc->ident = AT_UPPER; + #endif + arc->flow = arc->upper; + ( dummyArcsD + ( ( arc->tail ) - nodesD ) )->flow = + ( dummyArcsD + ( ( arc->tail ) - nodesD ) )->flow - arc->upper; + + ( dummyArcsD + ( ( arc->head ) - nodesD ) )->flow = + ( dummyArcsD + ( ( arc->head ) - nodesD ) )->flow + arc->upper; + } + } + + dummyRootD->balance = 0; + dummyRootD->prevInT = NULL; + dummyRootD->nextInT = nodesD; + dummyRootD->enteringTArc = NULL; + #if( QUADRATICCOST ) + dummyRootD->sumQuadratic = 0; + #endif + dummyRootD->potential = MAX_ART_COST; + dummyRootD->subTreeLevel = 0; + for( node = nodesD ; node != stopNodesD ; node++ ) { // initialize nodes + node->prevInT = node - 1; + node->nextInT = node + 1; + node->enteringTArc = dummyArcsD + ( node - nodesD ); + #if( QUADRATICCOST ) + node->sumQuadratic = ( node->enteringTArc )->quadraticCost; + #endif + node->potential = 0; + node->subTreeLevel = 1; + node->whenInT2 = 0; + } + + nodesD->prevInT = dummyRootD; + ( nodesD + n - 1 )->nextInT = NULL; + } + +/*--------------------------------------------------------------------------*/ + +void MCFSimplex::CreateAdditionalDualStructures( void ) +{ + // this method creates, in a Dual context, the Backward Star and the + // Forward Star of every node + + for( nodeDType *node = nodesD ; node != stopNodesD ; node++) { + // initialize nodes + node->firstBs = NULL; + node->firstFs = NULL; + node->numArcs = 0; + } + + dummyRootD->firstBs = NULL; + dummyRootD->firstFs = NULL; + dummyRootD->numArcs = 0; + for( arcDType *arc = arcsD ; arc != stopArcsD ; arc++ ) { + // initialize real arcs + arc->nextFs = ( arc->tail )->firstFs; + ( arc->tail )->firstFs = arc; + arc->nextBs = ( arc->head )->firstBs; + ( arc->head )->firstBs = arc; + ( arc->tail )->numArcs++; + ( arc->head )->numArcs++; + } + + ResetWhenInT2(); + } + +/*--------------------------------------------------------------------------*/ + +void MCFSimplex::PrimalSimplex( void ) +{ + #if( UNIPI_PRIMAL_INITIAL_SHOW == 0 ) + #if( UNIPI_PRIMAL_ITER_SHOW == 0 ) + #if( UNIPI_PRIMAL_FINAL_SHOW == 0 ) + //cout << endl; + #endif + #endif + #endif + #if( UNIPI_PRIMAL_INITIAL_SHOW ) + cout << endl; + for( int t = 0; t < 3; t++ ) + cout << "\t"; + cout << "PRIMALE MCFSimplex: ARCHI E NODI ALL' INIZIO" << endl; + ShowSituation( 3 ); + #endif + #if( QUADRATICCOST ) + #if( LIMITATEPRECISION ) + foValue = GetFO(); + int cont = 0; + #endif + #endif + + status = kUnSolved; + if( pricingRule != kCandidateListPivot ) + arcToStartP = arcsP; + + iterator = 0; // setting the initial arc for the Dantzig or First Elibigle Rule + + arcPType *enteringArc; + arcPType *leavingArc; + if( pricingRule == kCandidateListPivot ) + InitializePrimalCandidateList(); + + while( status == kUnSolved ) { + iterator++; + switch( pricingRule ) { + case( kDantzig ): + enteringArc = RuleDantzig(); + break; + case( kFirstEligibleArc ): + enteringArc = PRuleFirstEligibleArc(); + break; + case( kCandidateListPivot ): + enteringArc = RulePrimalCandidateListPivot(); + break; + } + + #if( QUADRATICCOST ) + #if( LIMITATEPRECISION ) + /* In the quadratic case with LIMITATEPRECISION == 1, the entering arcs + are selected according to a thresold. + This thresold is definited according to the old f.o. value. + If Primal Simplex doesn't find an entering arc, it calculates again + the f.o. value, and try again. */ + if( enteringArc == NULL ) { + foValue = GetFO(); + switch( pricingRule ) { + case( kDantzig ): + enteringArc = RuleDantzig(); + break; + case( kFirstEligibleArc ): + enteringArc = PRuleFirstEligibleArc(); + break; + case( kCandidateListPivot ): + enteringArc = RulePrimalCandidateListPivot(); + break; + } + } + #endif + #endif + + if( pricingRule != kCandidateListPivot ) { + // in every iteration the algorithm changes the initial arc for + // Dantzig and First Eligible Rule. + arcToStartP++; + if( arcToStartP == stopArcsP ) + arcToStartP = arcsP; + } + + if( enteringArc ) { + arcPType *arc; + nodePType *k1; + nodePType *k2; + /* If the reduced cost of the entering arc is > 0, the Primal Simplex + pushes flow in the cycle determinated by T and entering arc for decreases + flow in the entering arc: in the linear case entering arc's flow goes to 0, + in the quadratic case it decreases while it's possibile. + If the reduced cost of the entering arc is < 0, the Primal Simplex pushes + flow in the cycle determinated by T and entering arc for increases flow + in the entering arc: in the linear case entering arc's flow goes to upper + bound, in the quadratic case it increases while it's possibile. */ + + #if( QUADRATICCOST ) + FONumber t; + FONumber theta; + FONumber deltaFO; + FNumber theta2; + CNumber Q = ( enteringArc->tail )->sumQuadratic + + ( enteringArc->head )->sumQuadratic + enteringArc->quadraticCost; + // Q is the sum of the quadratic coefficient in the cycle determinated by T + // and entering arc. + FONumber rc = ReductCost( enteringArc ); + if( ETZ( Q, EpsCst ) ) + theta = Inf(); // This value will be certainly decreased + else + theta = ABS( rc / Q ); + // This is the best theta value (with best f.o. value decrease) + + leavingArc = enteringArc; + nodePType *cycleRoot; // The radix of the cycle determinated by T and entering arc. + if( GTZ( rc , EpsCst ) ) { + #else + FNumber t; + FNumber theta; + if( enteringArc->ident == AT_UPPER ) { + #endif + /* Primal Simplex increases or decreases entering arc's flow. + "theta" is a positive value. + For this reason the algorithm uses two nodes ("k1" and "k2") to push + flow ("theta") from "k1" to "k2". + According to entering arc's reduct cost, the algorithm determinates + "k1" and "k2" */ + + k1 = enteringArc->head; + k2 = enteringArc->tail; + #if( QUADRATICCOST ) + theta = min( theta , enteringArc->flow ); + // The best value for theta is compared with the entering arc's bound + theta2 = - theta; + #else + theta = enteringArc->flow; + #endif + } + else { + k1 = enteringArc->tail; + k2 = enteringArc->head; + #if( QUADRATICCOST ) + theta = min( theta , enteringArc->upper - enteringArc->flow ); + // The best value for theta is compared with the entering arc's bound + theta2 = theta; + #else + theta = enteringArc->upper - enteringArc->flow; + #endif + } + + nodePType *memK1 = k1; + nodePType *memK2 = k2; + leavingArc = NULL; + #if( QUADRATICCOST ) + #if( LIMITATEPRECISION ) + deltaFO = rc * theta2 + Q * theta2 * theta2 / 2; + #endif + bool leavingReducesFlow = GTZ( rc , EpsCst ); + #else + bool leavingReducesFlow = GTZ( ReductCost( enteringArc ) , EpsCst ); + #endif + // Compute "theta", find outgoing arc and "root" of the cycle + bool leave; + // Actual "theta" is compared with the bounds of the other cycle's arcs + while( k1 != k2 ) { + if( k1->subTreeLevel > k2->subTreeLevel ) { + arc = k1->enteringTArc; + if( arc->tail != k1 ) { + t = arc->upper - arc->flow; + leave = false; + } + else { + t = arc->flow; + leave = true; + } + + if( t < theta ) { + // The algorithm has found a possible leaving arc + theta = t; + leavingArc = arc; + leavingReducesFlow = leave; + // If "leavingReducesFlow" == true, if this arc is selected to exit the base, + // it decreases its flow + } + + k1 = Father( k1 , arc ); + } + else { + arc = k2->enteringTArc; + if( arc->tail == k2 ) { + t = arc->upper - arc->flow; + leave = false; + } + else { + t = arc->flow; + leave = true; + } + + if( t <= theta ) { + // The algorithm has found a possible leaving arc + theta = t; + leavingArc = arc; + leavingReducesFlow = leave; + // If "leavingReducesFlow" == true, if this arc is selected to exit the base, + // it decreases its flow + } + + k2 = Father(k2, arc); + } + } + + #if( QUADRATICCOST ) + cycleRoot = k1; + #endif + + if( leavingArc == NULL ) + leavingArc = enteringArc; + + if( theta >= Inf() ) { + status = kUnbounded; + break; + } + + // Update flow with "theta" + k1 = memK1; + k2 = memK2; + #if( QUADRATICCOST ) + if( enteringArc->tail == k1 ) + theta2 = theta; + else + theta2 = -theta; + + // "theta" is a positive value in every case. + // "theta2" is the real theta value according to the real + // direction of the entering arc + #if( LIMITATEPRECISION ) + deltaFO = rc * theta2 + Q * theta2 * theta2 / 2; + // The decrease of the f.o. value in the quadratic case + #endif + #endif + + if( ! ETZ(theta , EpsFlw ) ) { + if( enteringArc->tail == k1 ) + enteringArc->flow = enteringArc->flow + theta; + else + enteringArc->flow = enteringArc->flow - theta; + + while( k1 != k2 ) { + if( k1->subTreeLevel > k2->subTreeLevel ) { + arc = k1->enteringTArc; + if( arc->tail != k1 ) + arc->flow = arc->flow + theta; + else + arc->flow = arc->flow - theta; + + k1 = Father(k1, k1->enteringTArc); + } + else { + arc = k2->enteringTArc; + if( arc->tail == k2 ) + arc->flow = arc->flow + theta; + else + arc->flow = arc->flow - theta; + + k2 = Father(k2, k2->enteringTArc); + } + } + } + + if( enteringArc != leavingArc ) { + bool leavingBringFlowInT2 = ( leavingReducesFlow == + ( ( leavingArc->tail )->subTreeLevel > ( leavingArc->head )->subTreeLevel ) ); + // "leavingBringFlowInT2" == true if leaving arc brings flow to the subtree T2 + if( leavingBringFlowInT2 == ( memK1 == enteringArc->tail ) ) { + k2 = enteringArc->tail; + k1 = enteringArc->head; + } + else { + k2 = enteringArc->head; + k1 = enteringArc->tail; + } + } + + #if( QUADRATICCOST == 0 ) + if( leavingReducesFlow ) + leavingArc->ident = AT_LOWER; + else + leavingArc->ident = AT_UPPER; + + if( leavingArc != enteringArc ) { + enteringArc->ident = BASIC; + nodePType *h1; + nodePType *h2; + // "h1" is the node in the leaving arc with smaller tree's level + if( ( leavingArc->tail )->subTreeLevel < ( leavingArc->head )->subTreeLevel ) { + h1 = leavingArc->tail; + h2 = leavingArc->head; + } + else { + h1 = leavingArc->head; + h2 = leavingArc->tail; + } + + UpdateT(leavingArc, enteringArc, h1, h2, k1, k2); + // Update potential of the subtree T2 + k2 = enteringArc->head; + CNumber delta = ReductCost(enteringArc); + if( ( enteringArc->tail )->subTreeLevel > ( enteringArc->head )->subTreeLevel ) { + delta = -delta; + k2 = enteringArc->tail; + } + + AddPotential( k2 , delta ); + // In the linear case Primal Simplex only updates the potential of the nodes of + // subtree T2 + } + #else + if( leavingArc != enteringArc ) { + nodePType *h1; + nodePType *h2; + // "h1" is the node in the leaving arc with smaller tree's level + if( ( leavingArc->tail )->subTreeLevel < + ( leavingArc->head )->subTreeLevel ) { + h1 = leavingArc->tail; + h2 = leavingArc->head; + } + else { + h1 = leavingArc->head; + h2 = leavingArc->tail; + } + + // Update the basic tree + UpdateT( leavingArc , enteringArc , h1 , h2 , k1 , k2 ); + } + + #if( OPTQUADRATIC ) + nodePType *h1; + nodePType *h2; + if( ( leavingArc->tail )->subTreeLevel < + ( leavingArc->head )->subTreeLevel ) { + h1 = leavingArc->tail; + h2 = leavingArc->head; + } + else { + h1 = leavingArc->head; + h2 = leavingArc->tail; + } + + nodePType *node = h1; + nodePType *updateNode = h1; + if( h1 == cycleRoot ) + ComputePotential( cycleRoot ); + else { + while( node != cycleRoot ) { + arcPType *entArc = node->enteringTArc; + if( ! ETZ( entArc->quadraticCost , EpsCst ) ) + updateNode = node; + + node = Father( node , entArc ); + } + + ComputePotential( updateNode ); + node = h2; + updateNode = h2; + while( node != cycleRoot ) { + arcPType *entArc = node->enteringTArc; + if( ! ETZ( entArc->quadraticCost , EpsCst ) ) + updateNode = node; + + node = Father( node , entArc ); + } + + ComputePotential( updateNode ); + } + #else + // Update the potential of the node "manually" + ComputePotential( cycleRoot ); + #endif + + #if( LIMITATEPRECISION ) + cont = cont + 1; + if( cont == recomputeFOLimits ) { + cont = 0; + foValue = GetFO(); // Calculate f.o. value manually + } + else + foValue = foValue + deltaFO; + // Calculate the f.o. value with the estimated decrease + #endif + #endif + } + else { + status = kOK; + // If one of dummy arcs has flow bigger than 0, the solution is unfeasible. + for( arcPType *arc = dummyArcsP ; arc != stopDummyP ; arc++ ) + if( GTZ( arc->flow , EpsFlw ) ) + status = kUnfeasible; + } + + if( ( status == kUnSolved ) && MaxTime && MCFt ) { + double tu, ts; + TimeMCF( tu , ts ); + if( MaxTime < tu + ts ) + status = kStopped; + } + + if( ( status == kUnSolved ) && MaxIter) + if( MaxIter < (int) iterator ) + status = kStopped; + + #if( UNIPI_PRIMAL_ITER_SHOW ) + int it = (int) iterator; + if( it % UNIPI_PRIMAL_ITER_SHOW == 0 ) { + cout << endl; + for( int t = 0; t < 3; t++ ) + cout << "\t"; + cout << "PRIMALE MCFSimplex: ARCHI E NODI ALLA " << it << " ITERAZIONE" << endl; + ShowSituation( 3 ); + #if( FOSHOW ) + if( (int) iterator % FOSHOW == 0 ) + clog << "Iteration = " << iterator << " of = " + #if( LIMITATEPRECISION && QUADRATICCOST ) + << foValue + #else + << GetFO() + #endif + << endl; + #endif + } + #endif + } + + #if( UNIPI_PRIMAL_FINAL_SHOW ) + cout << endl; + for( int t = 0; t < 3; t++ ) + cout << "\t"; + cout << "PRIMALE UniPi: ARCHI E NODI ALLA FINE" << endl; + ShowSituation( 3 ); + #endif + + } // end( PrimalSimplex ) + +/*--------------------------------------------------------------------------*/ + +void MCFSimplex::DualSimplex( void ) +{ + #if( UNIPI_PRIMAL_INITIAL_SHOW == 0 ) + #if( UNIPI_PRIMAL_ITER_SHOW == 0 ) + #if( UNIPI_PRIMAL_FINAL_SHOW == 0 ) + cout << endl; + #endif + #endif + #endif + #if( UNIPI_DUAL_INITIAL_SHOW ) + cout << endl; + for( int t = 0; t < 3; t++ ) + cout << "\t"; + cout << "DUALE MCFSimplex: ARCHI E NODI ALL' INIZIO" << endl; + ShowSituation( 3 ); + #endif + + if( pricingRule != kCandidateListPivot ) + arcToStartD = arcsD; + + iterator = 0; + arcDType *enteringArc; + arcDType *leavingArc; + if( pricingRule == kCandidateListPivot ) + InitializeDualCandidateList(); + + status = kUnSolved; + while( status == kUnSolved ) { + iterator++; + if( iterator == Inf() ) { + ResetWhenInT2(); // Restore to 0 every nodes' field "whenInT2" + iterator = 1; + } + + switch( pricingRule ) { + case( kDantzig ): + leavingArc = DRuleFirstEligibleArc(); + break; // si esegue cmq FEA + case( kFirstEligibleArc ): + leavingArc = DRuleFirstEligibleArc(); + break; + case( kCandidateListPivot ): + leavingArc = RuleDualCandidateListPivot(); + break; + } + + if( pricingRule != kCandidateListPivot ) { + arcToStartD++; + if( arcToStartD == stopArcsD ) + arcToStartD = dummyArcsD; + + if( arcToStartD == stopDummyD ) + arcToStartD = arcsD; + // Setting the initial arc for the Dantzig or First Elibigle Rule + } + + if( leavingArc ) { + bool leavingArcInL = false; + bool leavingArcFromT1toT2; + if( LTZ( leavingArc->flow , EpsFlw ) ) + leavingArcInL = true; + + nodeDType *h1; + nodeDType *h2; + if( ( leavingArc->tail )->subTreeLevel < + ( leavingArc->head )->subTreeLevel ) { + h1 = leavingArc->tail; + h2 = leavingArc->head; + leavingArcFromT1toT2 = true; + } + else { + h1 = leavingArc->head; + h2 = leavingArc->tail; + leavingArcFromT1toT2 = false; + } + + Index numOfT2Arcs = 0; + int level = h2->subTreeLevel; + nodeDType *node = h2; + node->whenInT2 = iterator; + nodeDType *lastNodeOfT2 = h2; + numOfT2Arcs = node->numArcs; + while( node->nextInT && ( ( node->nextInT )->subTreeLevel > level ) ) { + node = node->nextInT; + lastNodeOfT2 = node; + numOfT2Arcs = numOfT2Arcs + node->numArcs; + node->whenInT2 = iterator; + } + + /* The Dual Simplex has determinated the leaving arc, and so the + subtrees T1 and T2. Dual Simplex scans T2 to fix the fields "whenInT2" + of T2's nodes to the iteration value, and counts the entering and + outgoing arcs from these nodes. According to this number, it decides + to scan the Backward and Forward of the subtree (T1 or T2) with the + minor number of entering/outgoing arcs from its nodes. */ + enteringArc = NULL; + bool lv = ( leavingArcFromT1toT2 == leavingArcInL ); + CNumber maxRc = Inf(); + //Search arc in the Forward Star and Backward Star of nodes of T1 + if( numOfT2Arcs > m ) { + // Dual Simplex starts from the node which follows the dummy root. + node = dummyRootD->nextInT; + bool fine = false; + while( fine == false ) { + /* If node is the root of subtree T2, Dual Simplex jumps to the node + (if exists) which follows the last node of T2 */ + if( node == h2 ) + if( lastNodeOfT2->nextInT ) + node = lastNodeOfT2->nextInT; + else + break; + + // Search arc in the Backward Star of nodes of T1 + arcDType *arc = node->firstBs; + while( arc ) { + if( ( arc->tail )->whenInT2 == iterator ) { + // Evaluate each arc from T2 to T1 which isn't in T + if( arc->ident == AT_LOWER ) { + if( lv ) { + CNumber rc = ABS( ReductCost( arc ) ); + if( LT( rc , maxRc , EpsCst ) ) { + enteringArc = arc; + maxRc = rc; + /* If arc is appropriate to enter in T and its reduct cost is 0, + search is ended: this is the arc which enters in T */ + if( ETZ( rc , EpsCst) ) { + fine = true; + break; + } + } + } + } + + if( arc->ident == AT_UPPER ) { + if( ! lv ) { + CNumber rc = ABS( ReductCost( arc ) ); + if( LT( rc , maxRc , EpsCst ) ) { + enteringArc = arc; + maxRc = rc; + /* If arc is appropriate to enter in T and its reduct cost is 0, + search is ended: this is the arc which enters in T */ + + if( ETZ( rc , EpsCst ) ) { + fine = true; + break; + } + } + } + } + } + + arc = arc->nextBs; + } + + // Search arc in the Forward Star of nodes of T1 + arc = node->firstFs; + while( arc ) { + if( ( arc->head )->whenInT2 == iterator ) { + // Evaluate each arc from T1 to T2 which isn't in T + if( arc->ident == AT_LOWER ) { + if( ! lv ) { + CNumber rc = ABS( ReductCost( arc ) ); + if( LT( rc , maxRc , EpsCst ) ) { + enteringArc = arc; + maxRc = rc; + /* If arc is appropriate to enter in T and its reduct cost is 0, + search is ended: this is the arc which enters in T */ + if( ETZ( rc , EpsCst ) ) { + fine = true; + break; + } + } + } + } + + if( arc->ident == AT_UPPER ) { + if( lv ) { + CNumber rc = ABS( ReductCost( arc ) ); + if( LT( rc , maxRc , EpsCst ) ) { + enteringArc = arc; + maxRc = rc; + /* If arc is appropriate to enter in T and its reduct cost is 0, + search is ended: this is the arc which enters in T */ + if( ETZ( rc , EpsCst ) ) { + fine = true; + break; + } + } + } + } + } + + arc = arc->nextFs; + } + + node = node->nextInT; + if( node == NULL ) + fine = true; + } + } + // Search arc in the Forward Star and Backward Star of nodes of T2 + else { + node = h2; + bool fine = false; + while( fine == false ) { + // Search arc in the Backward Star of nodes of T2 + arcDType *arc = node->firstBs; + CNumber rc; + while( arc ) { + if( ( arc->tail )->whenInT2 != iterator ) { + // Evaluate each arc from T1 to T2 which isn't in T + if( arc->ident == AT_LOWER ) { + if( ! lv ) { + rc = ABS( ReductCost( arc ) ); + if( LT( rc , maxRc , EpsCst ) ) { + enteringArc = arc; + maxRc = rc; + /* If arc is appropriate to enter in T and its reduct cost is 0, + search is ended: this is the arc which enters in T */ + if( ETZ( rc , EpsCst ) ) { + fine = true; + break; + } + } + } + } + + if( arc->ident == AT_UPPER ) { + if( lv ) { + rc = ABS( ReductCost( arc ) ); + if( LT( rc , maxRc , EpsCst ) ) { + enteringArc = arc; + maxRc = rc; + /* If arc is appropriate to enter in T and its reduct cost is 0, + search is ended: this is the arc which enters in T */ + if( ETZ( rc , EpsCst ) ) { + fine = true; + break; + } + } + } + } + } + + arc = arc->nextBs; + } + + // Search arc in the Forward Star of nodes of T2 + arc = node->firstFs; + while( arc ) { + if( ( arc->head )->whenInT2 != iterator ) { + // Evaluate each arc from T2 to T1 which isn't in T + if( arc->ident == AT_LOWER ) { + if( lv ) { + rc = ABS( ReductCost( arc ) ); + if( LT( rc , maxRc , EpsCst ) ) { + enteringArc = arc; + maxRc = rc; + /* If arc is appropriate to enter in T and its reduct cost is 0, + search is ended: this is the arc which enters in T */ + if( ETZ( rc , EpsCst ) ) { + fine = true; + break; + } + } + } + } + + if( arc->ident == AT_UPPER ) { + if( ! lv ) { + rc = ABS( ReductCost( arc ) ); + if( LT( rc , maxRc , EpsCst ) ) { + enteringArc = arc; + maxRc = rc; + /* If arc is appropriate to enter in T and its reduct cost is 0, + search is ended: this is the arc which enters in T */ + if( ETZ( rc , EpsCst) ) { + fine = true; + break; + } + } + } + } + } + + arc = arc->nextFs; + } + + if( node == lastNodeOfT2 ) + fine = true; + else + node = node->nextInT; + + } + } + + if( enteringArc ) { + FNumber theta = -leavingArc->flow; + if( GTZ( leavingArc->flow , EpsFlw ) ) + theta = leavingArc->flow - leavingArc->upper; + // Initial value of theta is the infeasibility of the leaving arc + + FNumber t; + nodeDType *k1; + nodeDType *k2; + /* if entering arc is in U, Dual Simplex pushs flow in the cycle + determinated by T and entering arc for decreases flow in the entering arc: + if entering arc is in L, Dual Simplex pushs flow in the cycle + determinated by T and entering arc for increases flow in the entering arc: + Dual Simplex increases or decreases entering arc's flow. + theta is a positive value. + For this reason the algorithm uses two nodes (k1 and k2) to push flow + (theta) from k1 to k2. According to entering arc's reduct cost, the + algorithm determinates k1 and k2 */ + + if( enteringArc->ident == AT_UPPER ) { + k1 = enteringArc->head; + k2 = enteringArc->tail; + } + else { + k1 = enteringArc->tail; + k2 = enteringArc->head; + } + + nodeDType *memK1 = k1; + nodeDType *memK2 = k2; + arcDType *arc; + k1 = memK1; + k2 = memK2; + // Update the flow + while( k1 != k2 ) { + if( k1->subTreeLevel > k2->subTreeLevel ) { + arc = k1->enteringTArc; + if( arc->tail != k1 ) + arc->flow = arc->flow + theta; + else + arc->flow = arc->flow - theta; + + k1 = Father(k1, k1->enteringTArc); + } + else { + arc = k2->enteringTArc; + if( arc->tail == k2 ) + arc->flow = arc->flow + theta; + else + arc->flow = arc->flow - theta; + + k2 = Father( k2 , k2->enteringTArc ); + } + } + + if(leavingArcInL ) + leavingArc->ident = AT_LOWER; + else + leavingArc->ident = AT_UPPER; + + bool leavingBringFlowInT2 = ( leavingArcInL == + ( ( leavingArc->tail )->subTreeLevel > + ( leavingArc->head )->subTreeLevel ) ); + // leavingBringFlowInT2 == true if leaving arc brings flow to the subtree T2 + if( leavingBringFlowInT2 != ( memK1 == enteringArc->tail ) ) { + k2 = enteringArc->tail; + k1 = enteringArc->head; + } + else { + k2 = enteringArc->head; + k1 = enteringArc->tail; + } + + if( enteringArc->ident == AT_LOWER ) + enteringArc->flow = enteringArc->flow + theta; + else + enteringArc->flow = enteringArc->flow - theta; + + enteringArc->ident = BASIC; + UpdateT( leavingArc , enteringArc , h1 , h2 , k1 , k2 ); + // update potential of the subtree T2 + k2 = enteringArc->head; + CNumber delta = ReductCost( enteringArc ); + if( ( enteringArc->tail) ->subTreeLevel > + ( enteringArc->head )->subTreeLevel ) { + delta = -delta; + k2 = enteringArc->tail; + } + + // Dual Simplex only updates the potential of the T2's nodes + AddPotential( k2 , delta ); + } + else + status = kUnfeasible; + /* If Dual Simplex finds a leaving arc but it doesn't find an entering arc, + the algorithm stops. At this point Dual Simplex has an unfeasible primal + solution. */ + } + else { + status = kOK; + // If one of dummy arcs has flow different than 0, the solution is unfeasible. + for( arcDType *arc = dummyArcsD ; arc != stopDummyD ; arc++ ) + if( ! ETZ( arc->flow , EpsFlw ) ) { + status = kUnfeasible; + break; + } + } + + if( ( status == kUnSolved ) && MaxTime ) { + double tu, ts; + TimeMCF( tu , ts ); + if( MaxTime < tu + ts ) + status = kStopped; + } + + if( ( status == kUnSolved ) && MaxIter && MCFt ) + if( MaxIter < (int) iterator ) + status = kStopped; + + #if( UNIPI_DUAL_ITER_SHOW ) + if( (int) iterator % UNIPI_DUAL_ITER_SHOW == 0 ) { + cout << endl; + for( int t = 0; t < 3; t++ ) + cout << "\t"; + cout << "DUALE MCFSimplex: ARCHI E NODI ALLA " << iterator << " ITERAZIONE" + << endl; + ShowSituation( 3 ); + #if( FOSHOW ) + cout << "of = " << GetFO() << endl; + #endif + } + #endif + } + + #if( UNIPI_DUAL_ITER_SHOW ) + int it = (int) iterator; + if( it % UNIPI_DUAL_ITER_SHOW == 0 ) { + cout << endl; + for( int t = 0; t < 3; t++ ) + cout << "\t"; + cout << "DUALE MCFSimplex: ARCHI E NODI ALLA " << iterator << " ITERAZIONE" + << endl; + Showsituation( 3 ); + } + #endif + + } // end( DualSimplex ) + +/*--------------------------------------------------------------------------*/ + +template +void MCFSimplex::UpdateT( A *h , A *k , N *h1 , N *h2 , N *k1 , N *k2 ) +{ + /* In subtree T2 there is a path from node h2 (deepest node of the leaving + arc h and root of T2) to node k2 (deepest node of the leaving arc h and + coming root of T2). With the update of T, this path will be overturned: + node k2 will become the root of T2... + The subtree T2 must be reordered and the field "subTreeLevel", which + represents the depth in T of every node, of every T2's nodes is changed. + Variable delta represents the increase of "subTreeLevel" value for node + k2 and its descendants: probably this value is a negative value. */ + + int delta = (k1->subTreeLevel) + 1 - (k2->subTreeLevel); + N *root = k2; + N *dad; + + /*To reorder T2, the method analyses every nodes of the path h2->k2, starting + from k2. For every node, it moves the node's subtree from its original + position to a new appropriate position. In particular k2 and its subtree + (k2 is the new root of T2, so the first nodes of the new T2) will be moved + next to node k1 (new father of k2), the next subtree will be moved beside + the last node of k2's subtree.... + "previousNode" represents the node where the new subtree will be moved + beside in this iterative action. At the start "previousNode" is the node + k1 (T2 will be at the right of k1). */ + + N *previousNode = k1; + N *lastNode; + /* "arc1" is the entering arc in T (passed by parameters). + For every analysed node of path h2->k2, the method changes + "enteringTArc" but it must remember the old arc, which will be the + "enteringTArc" of the next analysed node. + At the start "arc1" is k (the new "enteringTArc" of k2). */ + + A *arc1 = k; + A *arc2; + bool fine = false; + while( fine == false ) { + // If analysed node is h2, this is the last iteration + if( root == h2 ) + fine = true; + + dad = Father( root , root->enteringTArc ); + // Cut the root's subtree from T and update the "subLevelTree" of its nodes + lastNode = CutAndUpdateSubtree( root , delta ); + // Paste the root's subtree in the right position; + PasteSubtree( root , lastNode , previousNode ); + // In the next iteration the subtree will be moved beside the last node of + // the actual analysed subtree. + previousNode = lastNode; + /* The increase of the subtree in the next iteration is different from + the actual increase: in particual the increase increases itself (+2 + at every iteration). */ + delta = delta + 2; + /* A this point "enteringTArc" of actual root is stored in "arc2" and + changed; then "arc1" and "root" are changed. */ + arc2 = root->enteringTArc; + root->enteringTArc = arc1; + arc1 = arc2; + root = dad; + } + } + +/*--------------------------------------------------------------------------*/ + +template +N* MCFSimplex::CutAndUpdateSubtree( N *root , int delta ) +{ + int level = root->subTreeLevel; + N *node = root; + // The root of this subtree is passed by parameters, the last node is searched. + while ( ( node->nextInT ) && ( ( node->nextInT )->subTreeLevel > level ) ) { + node = node->nextInT; + // The "subTreeLevel" of every nodes of subtree is updated + node->subTreeLevel = node->subTreeLevel + delta; + } + + root->subTreeLevel = root->subTreeLevel + delta; + /* The 2 neighbouring nodes of the subtree (the node at the left of the root + and the node at the right of the last node) is joined. */ + + if( root->prevInT ) + ( root->prevInT )->nextInT = node->nextInT; + if( node->nextInT ) + ( node->nextInT )->prevInT = root->prevInT; + + return( node ); // the method returns the last node of the subtree + } + +/*--------------------------------------------------------------------------*/ + +template +void MCFSimplex::PasteSubtree( N *root , N *lastNode , N *previousNode ) +{ + /* The method inserts subtree ("root" and "lastNode" are the extremity of the + subtree) after "previousNode". The method starts to identify the next node + of "previousNode" ("nextNode"), so it joins "root" with "previousNode" and + "lastNode" with "nextNode" (if exists). */ + + N *nextNode = previousNode->nextInT; + root->prevInT = previousNode; + previousNode->nextInT = root; + lastNode->nextInT = nextNode; + if( nextNode ) + nextNode->prevInT = lastNode; + } + +/*--------------------------------------------------------------------------*/ + +MCFSimplex::arcPType* MCFSimplex::RuleDantzig( void ) +{ + arcPType *arc = arcToStartP; + arcPType *enteringArc = NULL; + #if( QUADRATICCOST ) + /* In the quadratic case used type for reduct cost is FONumber. + Value "lim" is the fixed thresold for the decrease of the f.o. value */ + FONumber lim = EpsOpt * foValue / n; + FONumber RC; + FONumber maxValue = 0; + #else + CNumber RC; + CNumber maxValue = 0; + #endif + + do { + // The method analyses every arc + #if( QUADRATICCOST ) + RC = ReductCost( arc ); + FNumber theta; + /* If reduct cost of arc is lower than 0, the flow of the arc must increase. + If reduct cost of arc is bigger than 0, the flow of the arc must decrease. + "theta" is the difference between lower (upper) bound and the actual flow. + */ + + if( LTZ( RC , EpsCst ) ) + theta = arc->upper - arc->flow; + + if( GTZ( RC , EpsCst ) ) + theta = -arc->flow; + + // If it's possible to increase (or decrease) the flow in this arc + if( ! ETZ( theta , EpsFlw ) ) { + /* "Q" is the sum of the quadratic coefficient of the arc belonging the + T path from tail's arc to head's arc + "Q" is always bigger than 0 or equals to 0. + If "Q" > 0, the value - RC / Q is the increase (decrease) of the flow + with the best decrease of f.o. value. + - RC/ Q must be compare with "theta" to avoid that the best increase + (decrease) of the flow violates the bounds of the arc. + This confront determines "theta". */ + + CNumber Q = ( arc->tail )->sumQuadratic + ( arc->head )->sumQuadratic + + arc->quadraticCost; + + if( GTZ( Q , EpsCst ) ) + if( GTZ( theta , EpsFlw ) ) + theta = min( theta , - RC / Q ); + else + theta = max( theta , - RC / Q ); + + /* Calculate the estimate decrease of the f.o. value if this arc is + selected and flow is increased (decreased) by "theta" */ + + CNumber deltaFO = RC * theta + Q * theta * theta / 2; + // if deltaFO < 0 this arc is appropriate; if deltaFO is lower than + // old decrease value, arc is the best arc. + + if( deltaFO < maxValue ) { + maxValue = deltaFO; + enteringArc = arc; + } + } + #else + if( arc->ident > BASIC ) { + RC = ReductCost( arc ); + + if( ( LTZ( RC , EpsCst ) && ( arc->ident == AT_LOWER ) ) || + ( GTZ( RC , EpsCst ) && ( arc->ident == AT_UPPER ) ) ) { + + if( RC < 0 ) + RC = -RC; + + if( RC > maxValue ) { + maxValue = RC; + enteringArc = arc; + } + } + } + #endif + + arc++; + if( arc == stopArcsP ) + arc = arcsP; + + } while( arc != arcToStartP ); + + #if( ( LIMITATEPRECISION ) && ( QUADRATICCOST ) ) + if( -maxValue <= lim ) + enteringArc = NULL; + #endif + + return( enteringArc ); + } + +/*--------------------------------------------------------------------------*/ + +MCFSimplex::arcPType* MCFSimplex::PRuleFirstEligibleArc( void ) +{ + arcPType *arc = arcToStartP; + arcPType *enteringArc = NULL; + #if( QUADRATICCOST ) + FONumber RC; + #else + CNumber RC; + #endif + + do { + #if( QUADRATICCOST ) + // In this method the "decrease f.o. value" criterion is not used. + RC = ReductCost( arc ); + if( ( LTZ( RC , EpsCst ) && LT( arc->flow , arc->upper , EpsFlw ) ) || + ( GTZ( RC , EpsCst ) && GTZ( arc->flow , EpsFlw ) ) ) + enteringArc = arc; + #else + if( arc->ident > BASIC ) { + RC = ReductCost( arc ); + if( ( LTZ( RC , EpsCst ) && ( arc->ident == AT_LOWER ) ) || + ( GTZ( RC , EpsCst ) && ( arc->ident == AT_UPPER ) ) ) + enteringArc = arc; + } + #endif + + arc++; + if( arc == stopArcsP ) + arc = dummyArcsP; + if( arc == stopDummyP ) + arc = arcsP; + + } while( ( enteringArc == NULL ) && ( arc != arcToStartP ) ); + + return( enteringArc ); + } + +/*--------------------------------------------------------------------------*/ + +MCFSimplex::arcDType* MCFSimplex::DRuleFirstEligibleArc( void ) +{ + arcDType *arc = arcToStartD; + arcDType *leavingArc = NULL; + do { + if( GT( arc->flow , arc->upper , EpsFlw ) || LTZ( arc->flow , EpsFlw ) ) + leavingArc = arc; + + arc++; + if( arc == stopArcsD ) + arc = dummyArcsD; + if( arc == stopDummyD ) + arc = arcsD; + + } while( ( leavingArc == NULL ) && ( arc != arcToStartD ) ); + + return(leavingArc); + } + +/*--------------------------------------------------------------------------*/ + +MCFSimplex::arcPType* MCFSimplex::RulePrimalCandidateListPivot( void ) +{ + Index next = 0; + Index i; + Index minimeValue; + if( hotListSize < tempCandidateListSize ) + minimeValue = hotListSize; + else + minimeValue = tempCandidateListSize; + + #if( QUADRATICCOST ) + // Check if the left arcs in the list continue to violate the dual condition + for( i = 2 ; i <= minimeValue ; i++ ) { + arcPType *arc = candP[ i ].arc; + FONumber red_cost = ReductCost( arc ); + FNumber theta = 0; + /* If reduct cost of arc is lower than 0, the flow of the arc must increase. + If reduct cost of arc is bigger than 0, the flow of the arc must decrease. + "theta" is the difference between lower (upper) bound and the actual flow. + */ + + if( LTZ( red_cost , EpsCst ) ) + theta = arc->upper - arc->flow; + else + if( GTZ( red_cost , EpsCst ) ) + theta = - arc->flow; + + // if it's possible to increase (or decrease) the flow in this arc + if( theta != 0 ) { + /* "Q" is the sum of the quadratic coefficient of the arc belonging the T + path from tail's arc to head's arc + "Q" is always bigger than 0 or equals to 0. + If "Q" > 0, the value - RC / Q is the increase (decrease) of the flow + with the best decrease of f.o. value. + - RC/ Q must be compare with "theta" to avoid that the best increase + (decrease) of the flow violates the bounds of the arc. + This confront determines "theta". */ + + CNumber Q = ( arc->tail )->sumQuadratic + ( arc->head )->sumQuadratic + + arc->quadraticCost; + + if( GTZ( Q , EpsCst ) ) + if( GTZ( theta , EpsFlw ) ) + theta = min( theta , - red_cost / Q ); + else + theta = max( theta , - red_cost / Q ); + + /* Calculate the estimate decrease of the f.o. value if this arc is + selected and flow is increased (decreased) by "theta" */ + + CNumber deltaFO = red_cost * theta + Q * theta * theta / 2; + #if( LIMITATEPRECISION ) + // if deltaFO < 0 this arc is appropriate; if deltaFO is lower than + // old decrease value, arc is the best arc. + if( - deltaFO > ( EpsOpt * foValue / n ) ) { + #else + if( LTZ( deltaFO , EpsCst ) ) { + #endif + next++; + candP[ next ].arc = arc; + candP[ next ].absRC = -deltaFO; + } + } + } + + tempCandidateListSize = next; + Index oldGroupPos = groupPos; + // Search other arcs to fill the list + do { + arcPType *arc; + for( arc = arcsP + groupPos ; arc < stopArcsP ; arc += numGroup ) { + FONumber red_cost = ReductCost( arc ); + FNumber theta = 0; + /* If reduced cost is lower than 0, the flow of the arc must increase. + If reduced cost is larger than 0, the flow of the arc must decrease. + "theta" is the difference between lower (upper) bound and the actual + flow. */ + + if( LTZ( red_cost , EpsCst ) ) + theta = arc->upper - arc->flow; + else + if( GTZ( red_cost , EpsCst ) ) + theta = - arc->flow; + + // if it's possible to increase (or decrease) the flow in this arc + if( theta != 0 ) { + /* "Q" is the sum of the quadratic coefficient of the arc belonging the T + path from tail's arc to head's arc + "Q" is always bigger than 0 or equals to 0. + If "Q" > 0, the value - RC / Q is the increase (decrease) of the flow + with the best decrease of f.o. value. + - RC/ Q must be compare with "theta" to avoid that the best increase + (decrease) of the flow violates the bounds of the arc. + This confront determines "theta". */ + CNumber Q = ( arc->tail )->sumQuadratic + ( arc->head )->sumQuadratic + + arc->quadraticCost; + + if( GTZ( Q , EpsCst ) ) + if( GTZ( theta , EpsFlw ) ) + theta = min( theta , - red_cost / Q ); + else + theta = max( theta , - red_cost / Q ); + + /* Calculate the estimate decrease of the f.o. value if this arc is + selected and flow is increased (decreased) by "theta" */ + + CNumber deltaFO = red_cost * theta + Q * theta * theta / 2; + #if( LIMITATEPRECISION ) + // if deltaFO < 0 this arc is appropriate; if deltaFO is lower than + // old decrease value, arc is the best arc. + if( -deltaFO > ( EpsOpt * foValue / n ) ) { + #else + if( LTZ( deltaFO , EpsCst ) ) { + #endif + tempCandidateListSize++; + candP[ tempCandidateListSize ].arc = arc; + candP[ tempCandidateListSize ].absRC = -deltaFO; + } + } + } + + groupPos++; + if( groupPos == numGroup ) + groupPos = 0; + + } while( ( tempCandidateListSize < hotListSize ) && + ( groupPos != oldGroupPos ) ); + #else + // Check if the left arcs in the list continue to violate the dual condition + for( i = 2 ; i <= minimeValue ; i++ ) { + arcPType *arc = candP[i].arc; + CNumber red_cost = ReductCost( arc ); + + if( ( LTZ( red_cost , EpsCst ) && ( arc->ident == AT_LOWER ) ) || + ( GTZ( red_cost , EpsCst ) && ( arc->ident == AT_UPPER ) ) ) { + next++; + candP[ next ].arc = arc; + candP[ next ].absRC = ABS( red_cost ); + } + } + + tempCandidateListSize = next; + Index oldGroupPos = groupPos; + // Search other arcs to fill the list + do { + arcPType *arc; + for( arc = arcsP + groupPos ; arc < stopArcsP ; arc += numGroup ) { + if( arc->ident == AT_LOWER ) { + CNumber red_cost = ReductCost( arc ); + if( LTZ( red_cost , EpsCst ) ) { + tempCandidateListSize++; + candP[ tempCandidateListSize ].arc = arc; + candP[ tempCandidateListSize ].absRC = ABS( red_cost ); + } + } + else + if( arc->ident == AT_UPPER ) { + CNumber red_cost = ReductCost( arc ); + if( GTZ( red_cost , EpsCst ) ) { + tempCandidateListSize++; + candP[ tempCandidateListSize ].arc = arc; + candP[ tempCandidateListSize ].absRC = ABS( red_cost ); + } + } + } + + groupPos++; + if( groupPos == numGroup ) + groupPos = 0; + + } while( ( tempCandidateListSize < hotListSize ) && ( groupPos != oldGroupPos ) ); + #endif + + if( tempCandidateListSize ) { + SortPrimalCandidateList( 1 , tempCandidateListSize ); + return( candP[ 1 ].arc ); + } + else + return( NULL ); + } + +/*--------------------------------------------------------------------------*/ + +inline void MCFSimplex::InitializePrimalCandidateList( void ) +{ + numGroup = ( ( m - 1 ) / numCandidateList ) + 1; + groupPos = 0; + tempCandidateListSize = 0; + } + +/*--------------------------------------------------------------------------*/ + +inline void MCFSimplex::SortPrimalCandidateList( Index min , Index max ) +{ + Index left = min; + Index right = max; + #if( QUADRATICCOST ) + FONumber cut = candP[ ( left + right ) / 2 ].absRC; + #else + CNumber cut = candP[ ( left + right ) / 2 ].absRC; + #endif + do { + while( candP[ left ].absRC > cut) + left++; + while( cut > candP[ right ].absRC) + right--; + + if( left < right ) + Swap( candP[ left ] , candP[ right ] ); + + if(left <= right) { + left++; + right--; + } + } while( left <= right ); + + if( min < right ) + SortPrimalCandidateList( min , right ); + if( ( left < max ) && ( left <= hotListSize ) ) + SortPrimalCandidateList( left , max ); + } + +/*--------------------------------------------------------------------------*/ + +MCFSimplex::arcDType* MCFSimplex::RuleDualCandidateListPivot( void ) +{ + Index next = 0; + // Check if the left arcs in the list continue to violate the primal condition + for( Index i = 2 ; ( i <= hotListSize ) && ( i <= tempCandidateListSize ) ; + i++ ) { + nodeDType *node = candD[ i ].node; + arcDType *arc = node->enteringTArc; + cFNumber flow = arc->flow; + if( LTZ( flow , EpsFlw ) ) { + next++; + candD[ next ].node = node; + candD[ next ].absInfeas = ABS( flow ); + } + + if( GT( flow , arc->upper , EpsFlw ) ) { + next++; + candD[ next ].node = node; + candD[ next ].absInfeas = flow - arc->upper; + } + } + + tempCandidateListSize = next; + Index oldGroupPos = groupPos; + // Search other arcs to fill the list + do { + nodeDType *node = nodesD + groupPos; + for( node ; node < stopNodesD ; node += numGroup ) { + arcDType *arc = node->enteringTArc; + cFNumber flow = arc->flow; + if( LTZ( flow , EpsFlw ) ) { + tempCandidateListSize++; + candD[ tempCandidateListSize ].node = node; + candD[ tempCandidateListSize ].absInfeas = ABS( flow ); + } + + if( GT( flow , arc->upper , EpsFlw) ) { + tempCandidateListSize++; + candD[ tempCandidateListSize ].node = node; + candD[ tempCandidateListSize ].absInfeas = flow - arc->upper; + } + } + + groupPos++; + if( groupPos == numGroup ) + groupPos = 0; + + } while( ( tempCandidateListSize < hotListSize ) && + ( groupPos != oldGroupPos ) ); + + if( tempCandidateListSize ) { + SortDualCandidateList( 1 , tempCandidateListSize ); + return( (candD[ 1 ].node)->enteringTArc ); + } + else + return( NULL ); + } + +/*--------------------------------------------------------------------------*/ + +inline void MCFSimplex::InitializeDualCandidateList( void ) +{ + numGroup = ( ( n - 1 ) / numCandidateList ) + 1; + groupPos = 0; + tempCandidateListSize = 0; + } + +/*--------------------------------------------------------------------------*/ + +inline void MCFSimplex::SortDualCandidateList(Index min, Index max) +{ + Index left = min; + Index right = max; + FNumber cut = candD[ ( left + right ) / 2 ].absInfeas; + do { + while( candD[ left ].absInfeas > cut ) + left++; + while( cut > candD[ right ].absInfeas ) + right--; + if( left < right ) + Swap( candD[left ] , candD[ right ] ); + if( left <= right) { + left++; + right--; + } + } while( left <= right ); + + if( min < right ) + SortDualCandidateList( min , right ); + if( (left < max) && ( left <= hotListSize ) ) + SortDualCandidateList( left , max ); + } + +/*--------------------------------------------------------------------------*/ + +template +inline void MCFSimplex::AddPotential( N *r , RCT delta ) +{ + int level = r->subTreeLevel; + N *n = r; + + do { + n->potential = n->potential + delta; + n = n->nextInT; + } while ( ( n ) && ( n->subTreeLevel > level ) ); + } + +/*--------------------------------------------------------------------------*/ + +template +inline void MCFSimplex::ComputePotential( N *r ) +{ + N *n = r; + int level = r->subTreeLevel; + FONumber cost; + // If "n" is not the dummy root, the potential of "r" is computed. + // If "n" is the dummy root, the potential of dummy root is a constant. + + do { + if( n->enteringTArc ) { + cost = ( n->enteringTArc )->cost; + #if (QUADRATICCOST) + // Also field "sumQuadratic" is updated + n->sumQuadratic = ( Father( n , n->enteringTArc ) )->sumQuadratic + + ( n->enteringTArc )->quadraticCost; + + if( ! ETZ( ( n->enteringTArc )->flow , EpsFlw ) ) + cost = cost + ( ( n->enteringTArc )->quadraticCost * ( n->enteringTArc )->flow ); + #endif + + if( n == ( n->enteringTArc )->head ) + n->potential = ( Father( n , n->enteringTArc ) )->potential + cost; + else + n->potential = ( Father( n , n->enteringTArc ) )->potential - cost; + } + n = n->nextInT; + } while( ( n ) && ( n->subTreeLevel > level ) ); + } + +/*--------------------------------------------------------------------------*/ + +void MCFSimplex::CreateInitialPModifiedBalanceVector( void ) +{ + int i = 0; + delete[] modifiedBalance; + modifiedBalance = new FNumber[ n ]; + // Initialited every node's modifiedBalance to his balance + for ( nodePType *node = nodesP ; node != stopNodesP ; node++ ) { + modifiedBalance[i] = node->balance; + i++; + } + + // Modify the vector according to the arcs out of base with flow non zero + // Scan the real arcs + + for( arcPType *arc = arcsP ; arc != stopArcsP ; arc++ ) { + #if( QUADRATICCOST ) + if( ( ! ETZ( arc->flow , EpsFlw ) ) && + ( ( arc->tail )->enteringTArc != arc ) && + ( ( arc->head )->enteringTArc != arc ) ) { + i = (arc->tail) - nodesP; + modifiedBalance[ i ] += arc->flow; + i = (arc->head) - nodesP; + modifiedBalance[ i ] -= arc->flow; + } + #else + if( arc->ident == AT_UPPER ) { + i = (arc->tail) - nodesP; + modifiedBalance[ i ] += arc->upper; + i = (arc->head) - nodesP; + modifiedBalance[ i ] -= arc->upper; + } + #endif + } + + // Scan the dummy arcs + for( arcPType *arc = dummyArcsP ; arc != stopDummyP ; arc++ ) { + #if( QUADRATICCOST ) + if ( ( ! ETZ( arc->flow , EpsFlw ) ) && + ( ( arc->tail )->enteringTArc != arc ) && + ( ( arc->head )->enteringTArc != arc ) ) { + i = (arc->tail) - nodesP; + modifiedBalance[ i ] += arc->flow; + i = (arc->head) - nodesP; + modifiedBalance[ i ] -= arc->flow; + } + #else + if (arc->ident == AT_UPPER) { + i = (arc->tail) - nodesP; + modifiedBalance[ i ] += arc->upper; + i = (arc->head) - nodesP; + modifiedBalance[ i ] -= arc->upper; + } + #endif + } + } + +/*--------------------------------------------------------------------------*/ + +void MCFSimplex::PostPVisit( nodePType *r ) +{ + // The method controls if "r" is a leaf in T + bool rLeaf = false; + int i = r - nodesP; + if( r->nextInT ) + if( ( r->nextInT )->subTreeLevel <= r->subTreeLevel ) + rLeaf = true; + else + rLeaf = true; + + if( rLeaf ) // If "r" is a leaf + if( ( r->enteringTArc)->head == r ) // If enteringTArc of "r" goes in "r" + ( r->enteringTArc )->flow = modifiedBalance[ i ]; + else // If enteringTArc of "r" goes out "r" + ( r->enteringTArc )->flow = - modifiedBalance[ i ]; + else { // If "r" isn't a leaf + nodePType *desc = r->nextInT; + // Call PostPVisit for every child of "r" + while( ( desc ) && ( desc->subTreeLevel > r->subTreeLevel ) ) { + if( desc->subTreeLevel - 1 == r->subTreeLevel ) { // desc is a son of r + PostPVisit( desc ); + + if( ( desc->enteringTArc )->head == r ) // enteringTArc of desc goes in r + modifiedBalance[ i ] -= ( desc->enteringTArc )->flow; + else // If enteringTArc of "desc" goes out "r" + modifiedBalance[ i ] += ( desc->enteringTArc )->flow; + } + desc = desc->nextInT; + } + + if( r != dummyRootP ) + if( ( r->enteringTArc )->head == r ) // If enteringTArc of "r" goes in "r" + ( r->enteringTArc )->flow = modifiedBalance[ i ]; + else // If enteringTArc of "r" goes out "r" + ( r->enteringTArc )->flow = - modifiedBalance[ i ]; + } + } + +/*--------------------------------------------------------------------------*/ + +void MCFSimplex::BalanceFlow( nodePType *r ) +{ + // used only by Primal Simplex to restore a primal feasible solution. + if( r == dummyRootP ) { + nodePType *node = dummyRootP->nextInT; + while( node ) { + // call this function recursively for every son of dummy root + if( node->subTreeLevel == 1 ) + BalanceFlow( node ); + + node = node->nextInT; + } + } + else { + // The method controls if "r" is a leaf in T + bool rLeaf = false; + if( r->nextInT ) + if( ( r->nextInT )->subTreeLevel <= r->subTreeLevel ) + rLeaf = true; + else + rLeaf = true; + + if( rLeaf ) // If "r" is a leaf + AdjustFlow( r ); // The method controls if entering basic arc in "r" is + // not feasible; in case adjust its flow + else { // If "r" isn't a leaf + nodePType *node = r->nextInT; + // Balance the flow of every child of "r" + while ( ( node ) && ( node->subTreeLevel > r->subTreeLevel ) ) { + if( node->subTreeLevel == r->subTreeLevel + 1 ) + BalanceFlow( node ); + node = node->nextInT; + } + + // The method controls if entering basic arc in "r" is not feasible; + //in case adjust its flow + AdjustFlow( r ); + } + } + } + +/*--------------------------------------------------------------------------*/ + +void MCFSimplex::AdjustFlow( nodePType *r ) +{ + arcPType *arc = r->enteringTArc; + if( arc >= dummyArcsP ) // If entering arc of "r" is a dummy arc + if( LTZ( arc->flow , EpsFlw ) ) { + // If this dummy arc has flow < 0, the algorithm overturns the arc + nodePType *temp = arc->tail; + arc->tail = arc->head; + arc->head = temp; + arc->flow = -arc->flow; + } + else { // If entering arc of "r" is not a dummy arc + bool orientationDown = ( arc->head == r ); + FNumber delta = 0; + if( LTZ( arc->flow , EpsFlw ) ) { // If flow is < 0 + delta = -arc->flow; + arc->flow = 0; + #if( ! QUADRATICCOST ) + arc->ident = AT_LOWER; + #endif + } + + if( GT( arc->flow , arc->upper , EpsFlw ) ) { + // If flow goes over the capacity of the arc + delta = arc->upper - arc->flow; + arc->flow = arc->upper; + #if( ! QUADRATICCOST ) + arc->ident = AT_UPPER; + #endif + } + + /* This arc goes out from the basis, and the relative dummy arc goes in T. + Then the algorithm push flow in the cycle made by the arc and some arcs + of T to balance the flow. */ + + if( ! ETZ( delta , EpsFlw ) ) { + nodePType *node = Father( r , arc ); + while( node != dummyRootP ) { + arc = node->enteringTArc; + if( ( arc->head == node ) == orientationDown ) + arc->flow += delta; + else + arc->flow -= delta; + + node = Father( node , arc ); + } + + arcPType *dummy = dummyArcsP + ( r - nodesP ); + #if( ! QUADRATICCOST ) + dummy->ident = BASIC; + #endif + + /* Update the structure of the tree. If entering basic arc of "r" is + changed, subtree of "r"is moved next dummy root. */ + r->enteringTArc = dummy; + int deltaLevel = 1 - r->subTreeLevel; + nodePType *lastNode = CutAndUpdateSubtree( r , deltaLevel ); + PasteSubtree( r , lastNode , dummyRootP ); + if( ( dummy->head == r ) != orientationDown ) + dummy->flow += delta; + else + dummy->flow -= delta; + + if( LTZ( dummy->flow , EpsFlw ) ) { + nodePType *temp = dummy->tail; + dummy->tail = dummy->head; + dummy->head = temp; + dummy->flow = -dummy->flow; + } + } + } + } + +/*--------------------------------------------------------------------------*/ + +void MCFSimplex::CreateInitialDModifiedBalanceVector( void ) +{ + #if( ! QUADRATICCOST ) + int i = 0; + modifiedBalance = new FNumber[ n ]; + // Initialited every node's modifiedBalance to his balance + for( nodeDType *node = nodesD ; node != stopNodesD ; node++ ) { + modifiedBalance[ i ] = node->balance; + i++; + } + + // Modify the vector according to the arcs out of base with flow non zero + // Scan the real arcs + for( arcDType *arc = arcsD ; arc != stopArcsD ; arc++ ) + if( arc->ident == AT_UPPER ) { + i = (arc->tail) - nodesD; + modifiedBalance[ i ] += arc->upper; + i = (arc->head) - nodesD; + modifiedBalance[ i ] -= arc->upper; + } + + // Scan the dummy arcs + for( arcDType *arc = dummyArcsD ; arc != stopDummyD ; arc++ ) + if( arc->ident == AT_UPPER ) { + i = (arc->tail) - nodesD; + modifiedBalance[ i ] += arc->upper; + i = (arc->head) - nodesD; + modifiedBalance[ i ] -= arc->upper; + } + #endif + } + +/*--------------------------------------------------------------------------*/ + +void MCFSimplex::PostDVisit( nodeDType *r ) +{ + #if( ! QUADRATICCOST ) + // The method controls if "r" is a leaf in T + bool rLeaf = false; + int i = r - nodesD; + if( r->nextInT ) + if( ( r->nextInT )->subTreeLevel <= r->subTreeLevel ) + rLeaf = true; + else + rLeaf = true; + + if( rLeaf ) // If "r" is a leaf + if( ( r->enteringTArc)->head == r ) // If enteringTArc of "r" goes in "r" + ( r->enteringTArc )->flow = modifiedBalance[ i ]; + else // If enteringTArc of "r" goes out "r" + ( r->enteringTArc )->flow = - modifiedBalance[ i ]; + else { // If "r" isn't a leaf + nodeDType *desc = r->nextInT; + // Call PostDVisit for every child of "r" + while( ( desc ) && ( desc->subTreeLevel > r->subTreeLevel ) ) { + if( desc->subTreeLevel -1 == r->subTreeLevel ) { // desc is a son of r + PostDVisit( desc ); + + if( ( desc->enteringTArc )->head == r ) // enteringTArc of desc goes in r + modifiedBalance[ i ] -= ( desc->enteringTArc )->flow; + else // If enteringTArc of "desc" goes out "r" + modifiedBalance[ i ] += ( desc->enteringTArc )->flow; + } + + desc = desc->nextInT; + } + + if( r != dummyRootD ) + if( ( r->enteringTArc )->head == r ) // If enteringTArc of "r" goes in "r" + ( r->enteringTArc )->flow = modifiedBalance[ i ]; + else // If enteringTArc of "r" goes out "r" + ( r->enteringTArc )->flow = - modifiedBalance[ i ]; + } + #endif + } + +/*--------------------------------------------------------------------------*/ + +inline void MCFSimplex::ResetWhenInT2( void ) +{ + for( nodeDType *n = nodesD ; n != stopNodesD ; n++) + n->whenInT2 = 0; + } + +/*--------------------------------------------------------------------------*/ + +template +inline N* MCFSimplex::Father( N *n , A *a ) +{ + if( a == NULL ) + return NULL; + + if( a->tail == n ) + return( a->head ); + else + return( a->tail ); + } + +/*-------------------------------------------------------------------------*/ + +inline MCFSimplex::FONumber MCFSimplex::GetFO( void ) +{ + FONumber fo = 0; + if( usePrimalSimplex ) { + arcPType *arco; + for( arco = arcsP ; arco != stopArcsP ; arco++ ) { + #if( QUADRATICCOST ) + if( ! ETZ( arco->flow , EpsFlw ) ) + fo += arco->flow * ( arco->cost + arco->flow * arco->quadraticCost / 2 ); + #else + if( ( arco->ident == BASIC ) || ( arco->ident == AT_UPPER ) ) + fo += arco->cost * arco->flow; + #endif + } + + for( arco = dummyArcsP ; arco != stopDummyP ; arco++ ) { + #if( QUADRATICCOST ) + if( ! ETZ( arco->flow , EpsFlw ) ) + fo += arco->flow * ( arco->cost + arco->flow * arco->quadraticCost / 2 ); + #else + if( ( arco->ident == BASIC ) || ( arco->ident == AT_UPPER ) ) + fo += arco->cost * arco->flow; + #endif + } + } + else { + arcDType *a; + for( a = arcsD ; a != stopArcsD ; a++ ) { + #if (QUADRATICCOST) + fo += ( a->cost * a->flow ) + ( a->quadraticCost * a->flow * a->flow ) / 2; + #else + if( ( a->ident == BASIC ) || (a->ident == AT_UPPER ) ) + fo += a->cost * a->flow; + #endif + } + + for( a = dummyArcsD ; a != stopDummyD ; a++) { + #if (QUADRATICCOST) + fo += ( a->cost * a->flow ) + ( a->quadraticCost * a->flow * a->flow ) / 2; + #else + if( ( a->ident == BASIC ) || ( a->ident == AT_UPPER ) ) + fo += a->cost * a->flow; + #endif + } + } + + return( fo ); + } + +/*-------------------------------------------------------------------------*/ + +void MCFSimplex::PrintPNode( nodePType *nodo ) +{ + if( nodo ) + if( nodo != dummyRootP ) + cout << ( nodo - nodesP + 1 ); + else + cout << "r"; + else + cout << ".."; + } + +/*--------------------------------------------------------------------------*/ + +void MCFSimplex::PrintPArc( arcPType *arc ) +{ + if( arc ) { + cout << "("; + PrintPNode( arc->tail ); + cout << ", "; + PrintPNode( arc->head ); + cout << ")"; + } + else + cout << ".."; +} + +/*--------------------------------------------------------------------------*/ + +void MCFSimplex::PrintDNode( nodeDType *nodo ) +{ + if( nodo ) + if( nodo != dummyRootD ) + cout << ( nodo - nodesD + 1 ); + else + cout << "r"; + else + cout << ".."; + } + +/*--------------------------------------------------------------------------*/ + +void MCFSimplex::PrintDArc( arcDType *arc ) +{ + if( arc ) { + cout << "("; + PrintDNode( arc->tail ); + cout << ", "; + PrintDNode( arc->head ); + cout << ")"; + } + else + cout << ".."; + } + +/*--------------------------------------------------------------------------*/ + +MCFSimplex::nodePType* MCFSimplex::RecoverPNode( Index ind ) +{ + if( ( ind < 0 ) || ( ind > n ) ) + return( NULL ); + if( ind ) + return( nodesP + ind - 1 ); + else + return( dummyRootP ); + } + +/*--------------------------------------------------------------------------*/ + +MCFSimplex::arcPType* MCFSimplex::RecoverPArc( nodePType *tail , + nodePType *head ) +{ + if( ( tail == NULL ) || ( head == NULL ) ) + return( NULL ); + + arcPType *arc = arcsP; + while( ( arc->tail != tail ) || ( arc->head != head ) ) { + arc++; + if( arc == stopArcsP ) + arc = dummyArcsP; + if( arc == stopDummyP ) + return( NULL ); + } + + return( arc ); + } + +/*--------------------------------------------------------------------------*/ + +MCFSimplex::nodeDType* MCFSimplex::RecoverDNode( Index ind ) +{ + if( ( ind < 0 ) || ( ind > n ) ) + return( NULL ); + + if( ind ) + return( nodesD + ind - 1 ); + else + return( dummyRootD ); + } + +/*--------------------------------------------------------------------------*/ + +MCFSimplex::arcDType* MCFSimplex::RecoverDArc( nodeDType *tail , + nodeDType *head ) +{ + if( ( tail == NULL ) || ( head == NULL ) ) + return( NULL ); + + arcDType *arc = arcsD; + while( ( arc->tail != tail ) || ( arc->head != head ) ) { + arc++; + if( arc == stopArcsD ) + arc = dummyArcsD; + if( arc == stopDummyD ) + return( NULL ); + } + + return( arc ); + } + +/*--------------------------------------------------------------------------*/ + +void MCFSimplex::infoPNode( nodePType *node , int tab ) +{ + for( int t = 0 ; t < tab ; t++ ) + cout << "\t"; + cout << "Nodo "; + PrintPNode( node ); + cout << ": b = " << node->balance << " y = " << node->potential << endl; + #if( UNIPI_VIS_NODE_BASIC_ARC ) + cout << ": TArc="; + PrintPArc( node->enteringTArc ); + cout << endl; + #endif + } + +/*--------------------------------------------------------------------------*/ + +void MCFSimplex::infoPArc( arcPType *arc , int ind , int tab ) +{ + for( int t = 0 ; t < tab ; t++ ) + cout << "\t"; + cout << "Arco "; + PrintPArc( arc ); + cout << ": x = " << arc->flow; + #if( UNIPI_VIS_ARC_UPPER ) + cout << " u = " << arc->upper; + #endif + #if( UNIPI_VIS_ARC_COST ) + cout << " c = " << arc->cost; + #endif + #if( QUADRATICCOST ) + #if( UNIPI_VIS_ARC_Q_COST ) + cout << " q = " << arc->quadraticCost; + #endif + cout << endl; + for( int t = 0 ; t < tab ; t++ ) + cout << "\t"; + #if( UNIPI_VIS_ARC_REDUCT_COST ) + cout << " rc = " << MCFGetRC( ind ); + #endif + #else + cout << endl; + for( int t = 0 ; t < tab ; t++ ) + cout << "\t"; + #if( UNIPI_VIS_ARC_REDUCT_COST ) + cout << " rc = " << MCFGetRC( ind ); + #endif + #if( UNIPI_VIS_ARC_STATE ) + switch( arc->ident ) { + case( BASIC ): cout << " in T"; break; + case( AT_LOWER ): cout << " in L"; break; + case( AT_UPPER ): cout << " in U"; break; + case( DELETED ): cout << " canceled"; break; + case( CLOSED ): cout << " closed"; + } + #endif + #endif + cout << endl; + } + +/*--------------------------------------------------------------------------*/ + +void MCFSimplex::infoDNode( nodeDType *node , int tab ) +{ + for( int t = 0 ; t < tab; t++ ) + cout << "\t"; + cout << "Nodo "; + PrintDNode( node ); + cout << ": b = " << node->balance << " y = " << node->potential; + #if( UNIPI_VIS_NODE_BASIC_ARC ) + cout << ": TArc="; + PrintDArc( node->enteringTArc ); + cout << endl; + #endif + } + +/*--------------------------------------------------------------------------*/ + +void MCFSimplex::infoDArc( arcDType *arc , int ind , int tab ) +{ + for( int t = 0 ; t < tab ; t++ ) + cout << "\t"; + cout << "Arco "; + PrintDArc( arc ); + cout << " x = " << arc->flow; + #if( UNIPI_VIS_ARC_UPPER ) + cout << " u = " << arc->upper; + #endif + #if( UNIPI_VIS_ARC_COST ) + cout << " c = " << arc->cost; + #endif + #if( QUADRATICCOST ) + #if( UNIPI_VIS_ARC_Q_COST ) + cout << " q = " << arc->quadraticCost; + #endif + cout << endl; + for( int t = 0 ; t < tab ; t++ ) + cout << "\t"; + #if( UNIPI_VIS_ARC_REDUCT_COST ) + cout << " rc = " << MCFGetRC( ind ); + #endif + #else + cout << endl; + for( int t = 0 ; t < tab ; t++ ) + cout << "\t"; + #if( UNIPI_VIS_ARC_REDUCT_COST ) + cout << " rc = " << MCFGetRC( ind ); + #endif + #if (UNIPI_VIS_ARC_STATE) + switch( arc->ident ) { + case( BASIC ): cout << " in T"; break; + case( AT_LOWER ): cout << " in L"; break; + case( AT_UPPER ): cout << " in U"; break; + case( DELETED ): cout << " canceled"; break; + case( CLOSED ): cout << " closed"; + } + #endif + #endif + cout << endl; + } + +/*--------------------------------------------------------------------------*/ + +void MCFSimplex::ShowSituation( int tab ) +{ + if( usePrimalSimplex ) { + arcPType *arc; + nodePType *node; + int i = 0; + for( arc = arcsP ; arc != stopArcsP ; arc++ ) { + infoPArc( arc , i , tab ); + i++; + } + cout << endl; + #if( UNIPI_VIS_DUMMY_ARCS ) + i = 0; + for( arc = dummyArcsP ; arc != stopDummyP ; arc++ ) { + infoPArc( arc , i , tab ); + i++; + } + cout << endl; + #endif + infoPNode( dummyRootP , tab ); + for( node = nodesP ; node != stopNodesP ; node++ ) + infoPNode( node , tab ); + } + else { + arcDType *arc; + nodeDType *node; + int i = 0; + for( arc = arcsD ; arc != stopArcsD ; arc++ ) { + infoDArc( arc , i , tab ); + i++; + } + cout << endl; + #if( UNIPI_VIS_DUMMY_ARCS ) + i = 0; + for( arc = dummyArcsD ; arc != stopDummyD ; arc++) { + infoDArc( arc , i , tab ); + i++; + } + cout << endl; + #endif + infoDNode( dummyRootD , tab ); + for( node = nodesD ; node != stopNodesD ; node++ ) + infoDNode( node , tab ); + } + } + +/*-------------------------------------------------------------------------*/ +/*---------------------- End File MCFSimplex.C ----------------------------*/ +/*-------------------------------------------------------------------------*/ diff --git a/impl/MCFSimplex.h b/impl/MCFSimplex.h new file mode 100644 index 0000000..a8d84f6 --- /dev/null +++ b/impl/MCFSimplex.h @@ -0,0 +1,1086 @@ +/*--------------------------------------------------------------------------*/ +/*------------------------- File MCFSimplex.h ------------------------------*/ +/*--------------------------------------------------------------------------*/ +/** @file + * Linear and Quadratic Min Cost Flow problems solver based on the (primal and + * dual) simplex algorithm. Conforms to the standard MCF interface defined in + * MCFClass.h. + * + * \Version 1.00 + * + * \date 29 - 08 - 2011 + * + * \author Alessandro Bertolini \n + * Antonio Frangioni \n + * Operations Research Group \n + * Dipartimento di Informatica \n + * Universita' di Pisa \n + * + * Copyright © 2008 - 2011 by Alessandro Bertolini, Antonio Frangioni + */ +/*--------------------------------------------------------------------------*/ +/*--------------------------------------------------------------------------*/ +/*----------------------------- DEFINITIONS --------------------------------*/ +/*--------------------------------------------------------------------------*/ +/*--------------------------------------------------------------------------*/ + +#ifndef __MCFSimplex + #define __MCFSimplex /* self-identification: #endif at the end of the file */ + +/*--------------------------------------------------------------------------*/ +/*------------------------------ INCLUDES ----------------------------------*/ +/*--------------------------------------------------------------------------*/ + +#include "MCFClass.h" + +/*@} -----------------------------------------------------------------------*/ +/*--------------------------- NAMESPACE ------------------------------------*/ +/*--------------------------------------------------------------------------*/ + +#if( OPT_USE_NAMESPACES ) +namespace MCFClass_di_unipi_it +{ +#endif + +/*--------------------------------------------------------------------------*/ +/*-------------------------------- MACROS ----------------------------------*/ +/*--------------------------------------------------------------------------*/ +/** @defgroup MCFSimplex_MACROS Compile-time switches in MCFSimplex.h + There is only one macro in MCFSimplex, but it is very important! + @{ */ + +#define QUADRATICCOST 0 + +/**< Setting QUADRATICCOST == 1 the solver can solve problems with linear and + quadratic costs too (but the latter only with the Primal Simplex). + The reason for having a macro is that when quadratic costs are present the + "arcType" struct has the additional field "quadraticCost" to hold it. + Furthermore, the field "ident" is not created because the solver doesn't + use the classical TLU tripartition. Instead, closed arcs and deleted arcs + are characterized as follows: + - closed arcs have the field "cost" to INFINITY (Inf()); + - deleted arcs have the field "upper" to INFINITY and the "tail" and + "head" field are NULL. + Furthermore, the solver needs the variables "ignoredEnteringArc" and + "firstIgnoredEnteringArc", used to avoid nasty loops during the execution + of the Quadratic Primal Simplex algorithm. + If, instead, QUADRATICCOST == 0 then the solver can solve only problems + with linear costs. Hence, the field "quadraticCost" is useless and it + isn't created. Furthermore, Primal Simplex and Dual Simplex use the + tripartition TLU to divide the arcs, so the solver creates the field + "ident", which differentiates the set of the arcs in among deleted arcs, + closed arcs, arcs in T, arcs in L, arcs in U. + Thus, with QUADRATICCOST == 0 the solver cannot solve problems with + quadratic costs, but it does solve problems with linear costs faster. */ + +/*@} end( group( MCFCLASS_MACROS ) ) */ + +/*--------------------------------------------------------------------------*/ +/*---------------------------- CLASSES -------------------------------------*/ +/*--------------------------------------------------------------------------*/ +/** @defgroup MCFSimplex_CLASSES Classes in MCFSimplex.h + @{ */ + +/** The MCFSimplex class derives from the abstract base class MCFClass, thus + sharing its (standard) interface, and implements both the Primal and + Dual network simplex algorithms for solving (Linear and Quadratic) + Min Cost Flow problems */ + +class MCFSimplex: public MCFClass +{ + +/*--------------------------------------------------------------------------*/ +/*----------------------- PUBLIC PART OF THE CLASS -------------------------*/ +/*--------------------------------------------------------------------------*/ +/*-- --*/ +/*-- The following methods and data are the actual interface of the --*/ +/*-- class: the standard user should use these methods and data only. --*/ +/*-- --*/ +/*--------------------------------------------------------------------------*/ + + public: + +/*--------------------------------------------------------------------------*/ +/*---------------------------- PUBLIC TYPES --------------------------------*/ +/*--------------------------------------------------------------------------*/ + +/** Public enum describing the parameters of MCFSimplex. */ + + enum SimplexParam + { + kAlgPrimal = kLastParam , ///< parameter to set algorithm (Primal/Dual): + kAlgPricing , ///< parameter to set algorithm of pricing + kNumCandList , /**< parameter to set the number of candidate + list for Candidate List Pivot method */ + kHotListSize , /**< parameter to set the size of Hot List + for Candidate List Pivot method */ + kRecomputeFOLimits , /**< parameter to set the number of iterations + in which quadratic Primal Simplex computes + "manually" the f.o. value */ + kEpsOpt /**< parameter to set the precision of the + objective function value for the + quadratic Primal Simplex */ + }; + +/** Public enum describing the pricing rules in MCFSimplex::SetAlg(). */ + + enum enumPrcngRl + { + kDantzig = 0, ///< Dantzig's rule (most violated constraint) + kFirstEligibleArc , ///< First eligible arc in round-robin + kCandidateListPivot ///< Candidate List Pivot Rule + }; + +/*--------------------------------------------------------------------------*/ +/*--------------------------- PUBLIC METHODS -------------------------------*/ +/*--------------------------------------------------------------------------*/ +/*---------------------------- CONSTRUCTOR ---------------------------------*/ +/*--------------------------------------------------------------------------*/ + + MCFSimplex( cIndex nmx = 0 , cIndex mmx = 0 ); + +/**< Constructor of the class, as in MCFClass::MCFClass(). */ + +/*--------------------------------------------------------------------------*/ +/*-------------------------- OTHER INITIALIZATIONS -------------------------*/ +/*--------------------------------------------------------------------------*/ + + void LoadNet( cIndex nmx = 0 , cIndex mmx = 0 , cIndex pn = 0 , + cIndex pm = 0 , cFRow pU = NULL , cCRow pC = NULL , + cFRow pDfct = NULL , cIndex_Set pSn = NULL , + cIndex_Set pEn = NULL ); + +/**< Inputs a new network, as in MCFClass::LoadNet(). */ + +/*--------------------------------------------------------------------------*/ + + void SetAlg( bool UsPrml , char WhchPrc ); + +/**< Selects which algorithm (Primal vs Dual Network Simplex), and which + pricing rule within the algorithm, is used. + + If UsPrml == TRUE then the Primal Network Simplex algorithm is used, + otherwise the Dual Network Simplex is used. + + The allowed values for WhchPrc are: + + - kDantzig Dantzig's pricing rule, i.e., most violated dual + constraint; this can only be used with the Primal + Network Simplex + + - kFirstEligibleArcA "dumb" rule, first eligible arc in round-robin; + + - kCandidateListPivot Candidate List Pivot Rule + + If this method is *not* called, the Primal Network Simplex with the + Candidate List Pivot Rule (the best setting on most instances) is + used. */ + +/*--------------------------------------------------------------------------*/ + + void SetPar( int par , int val ); + +/**< Set general integer parameters. + + @param par is the parameter to set; since this method accepts an int + value, the enum SimplexParam can be used in addition to the + enum MCFParam to specify the integer parameters (every enum + is an int). + + @param value is the value to assign to the parameter. */ + +/*-------------------------------------------------------------------------*/ + + void SetPar( int par , double val ); + +/**< Set general float parameters. + + @param par is the parameter to set; since this method accepts an int + value, the enum SimplexParam can be used in addition to the + enum MCFParam to specify the float parameters (every enum + is an int). + + @param value is the value to assign to the parameter. */ + +/*--------------------------------------------------------------------------*/ +/*-------------------- METHODS FOR SOLVING THE PROBLEM ---------------------*/ +/*--------------------------------------------------------------------------*/ + + void SolveMCF( void ); + +/*--------------------------------------------------------------------------*/ +/*---------------------- METHODS FOR READING RESULTS -----------------------*/ +/*--------------------------------------------------------------------------*/ + + void MCFGetX( FRow F , Index_Set nms = NULL , + cIndex strt = 0 , Index stp = Inf() ); + +/*--------------------------------------------------------------------------*/ + + void MCFGetRC( CRow CR , cIndex_Set nms = NULL , + cIndex strt = 0 , Index stp = Inf() ) ; + + CNumber MCFGetRC( cIndex i ); + +/*--------------------------------------------------------------------------*/ + + void MCFGetPi( CRow P , cIndex_Set nms = NULL , + cIndex strt = 0 , Index stp = Inf() ); + +/*--------------------------------------------------------------------------*/ + + FONumber MCFGetFO( void ); + +/*--------------------------------------------------------------------------*/ +/*-------------- METHODS FOR READING THE DATA OF THE PROBLEM ---------------*/ +/*--------------------------------------------------------------------------*/ + + virtual void MCFArcs( Index_Set Startv , Index_Set Endv , + cIndex_Set nms = NULL , cIndex strt = 0 , + Index stp = Inf() ); + + inline Index MCFSNde( cIndex i ); + + inline Index MCFENde( cIndex i ); + +/*--------------------------------------------------------------------------*/ + + void MCFCosts( CRow Costv , cIndex_Set nms = NULL , + cIndex strt = 0 , Index stp = Inf() ); + + inline CNumber MCFCost( cIndex i ); + +/*--------------------------------------------------------------------------*/ + + void MCFQCoef( CRow Qv , cIndex_Set nms = NULL , + cIndex strt = 0 , Index stp = Inf() ); + + inline CNumber MCFQCoef( cIndex i ); + +/*--------------------------------------------------------------------------*/ + + void MCFUCaps( FRow UCapv , cIndex_Set nms = NULL , + cIndex strt = 0 , Index stp = Inf() ); + + inline FNumber MCFUCap( cIndex i ); + +/*--------------------------------------------------------------------------*/ + + void MCFDfcts( FRow Dfctv , cIndex_Set nms = NULL , + cIndex strt = 0 , Index stp = Inf() ); + + inline FNumber MCFDfct( cIndex i ); + +/*--------------------------------------------------------------------------*/ +/*------------- METHODS FOR ADDING / REMOVING / CHANGING DATA --------------*/ +/*--------------------------------------------------------------------------*/ +/*------- Changing the costs, QCoef, deficits and upper capacities ---------*/ +/*--------------------------------------------------------------------------*/ + + void ChgCosts( cCRow NCost , cIndex_Set nms = NULL , + cIndex strt = 0 , Index stp = Inf() ); + + void ChgCost( Index arc , cCNumber NCost ); + +/*--------------------------------------------------------------------------*/ + + void ChgQCoef( cCRow NQCoef = NULL , cIndex_Set nms = NULL , + cIndex strt = 0 , Index stp = Inf() ); + + void ChgQCoef( Index arc , cCNumber NQCoef ); + +/*--------------------------------------------------------------------------*/ + + void ChgDfcts( cFRow NDfct , cIndex_Set nms = NULL , + cIndex strt = 0 , Index stp = Inf() ); + + void ChgDfct( Index nod , cFNumber NDfct ); + +/*--------------------------------------------------------------------------*/ + + void ChgUCaps( cFRow NCap , cIndex_Set nms = NULL , + cIndex strt = 0 , Index stp = Inf() ); + + void ChgUCap( Index arc , cFNumber NCap ); + +/*--------------------------------------------------------------------------*/ +/*--------------- Modifying the structure of the graph ---------------------*/ +/*--------------------------------------------------------------------------*/ + + void CloseArc( cIndex name ); + + void DelNode( cIndex name ); + + bool IsClosedArc( cIndex name ); + + void OpenArc( cIndex name ) ; + + Index AddNode( cFNumber aDfct ); + + void ChangeArc( cIndex name , cIndex nSS = Inf() , + cIndex nEN = Inf() ); + + void DelArc( cIndex name ); + + Index AddArc( cIndex Start , cIndex End , cFNumber aU , cCNumber aC ); + + bool IsDeletedArc( cIndex name ); + +/*--------------------------------------------------------------------------*/ +/*------------------------------ DESTRUCTOR --------------------------------*/ +/*--------------------------------------------------------------------------*/ + + ~MCFSimplex(); + +/*--------------------------------------------------------------------------*/ +/*--------------------- PRIVATE PART OF THE CLASS --------------------------*/ +/*--------------------------------------------------------------------------*/ +/*-- --*/ +/*-- Nobody should ever look at this part: everything that is under this --*/ +/*-- advice may be changed without notice in any new release of the code. --*/ +/*-- --*/ +/*--------------------------------------------------------------------------*/ + + private: + +/*--------------------------------------------------------------------------*/ +/*-------------------------- PRIVATE DATA TYPES ----------------------------*/ +/*--------------------------------------------------------------------------*/ +/*-- --*/ +/*-- Let T \subseteq A be a spanning tree, and consider some node v \in V --*/ +/*-- \setminus { 0 }. There is an unique (undirected) path, denoted by --*/ +/*-- P(v), defined by T from v to the root node 0. The arc in P(v), which --*/ +/*-- is incident to v, is called the *basic arc* of v. The other terminal --*/ +/*-- node u of this basic arc is called the *father node* of v. The --*/ +/*-- spanning tree T is represented saving the basic arc of every node, --*/ +/*-- and maintaining the order of the nodes and the depth as to T root --*/ +/*-- after a Post-Visit of T. This order is saved in a bidirectional list --*/ +/*-- written in the node. --*/ +/*-- --*/ +/*-- The Primal Simplex uses a different data structure than the Dual --*/ +/*-- Simplex, because the Dual Simplex needs additional data (mainly the --*/ +/*-- Backward Star and Forward Star. Furthermore, the Primal Simplex uses --*/ +/*-- different data structures in the quadratic case. --*/ +/*-- --*/ +/*--------------------------------------------------------------------------*/ + + struct arcPType; // pre-declaration of the arc structure (pointers to arcs + // are contained in the node structure) for Primal Simplex + + struct arcDType; // pre-declaration of the arc structure (pointers to arcs + // are contained in the node structure) for Dual Simplex + + typedef double iteratorType; // type for the iteration counter and array + // "whenInT2" + + struct nodePType { // node structure for Primal Simplex - - - - - - - - + nodePType *prevInT; // previous node in the order of the Post-Visit on T + + nodePType *nextInT; // next node in the order of the Post-Visit on T + + arcPType *enteringTArc; // entering basic arc of this node + + FNumber balance; // supply/demand of this node; a node is called a + // supply node, a demand node, or a transshipment + // node depending upon whether balance is larger + // than, smaller than, or equal to zero + #if( QUADRATICCOST ) + CNumber sumQuadratic; // the sum of the quadratic coefficients of the tree's arcs + // from root of T to the node + + FONumber potential; // the node potential corresponding with the flow + // conservation constrait of this node + #else + CNumber potential; // the node potential corresponding with the flow + // conservation constrait of this node + #endif + + int subTreeLevel; // the depth of the node in T as to T root + + }; // end( struct( nodePType ) ) + + struct nodeDType { // node structure for Dual Simplex - - - - - - - - - + nodeDType *prevInT; // previous node in the order of the Post-Visit on T + + nodeDType *nextInT; // next node in the order of the Post-Visit on T + + arcDType *enteringTArc; // entering basic arc of this node + + FNumber balance; // supply/demand of this node; a node is called a + // supply node, a demand node, or a transshipment + // node depending upon whether balance is larger + // than, smaller than, or equal to zero + + #if( QUADRATICCOST ) + CNumber sumQuadratic; // the sum of the quadratic coefficients of the tree's arcs + // from root of T to the node + + FONumber potential; // the node potential corresponding with the flow + // conservation constrait of this node + #else + CNumber potential; // the node potential corresponding with the flow + // conservation constrait of this node + #endif + + int subTreeLevel; // the depth of the node in T as to T root + iteratorType whenInT2; // the last iteration where a node is in subtree T2 + + Index numArcs; // the number of the arcs which enter/exit from node + arcDType *firstBs; // the first arc in the node's Backward Star + arcDType *firstFs; // the first arc in the node's Forward Star + + }; // end( struct( nodeDType ) ) + + struct arcPType { // arc structure for Primal Simplex - - - - - - - - + nodePType *tail; // tail node + nodePType *head; // head node + FNumber flow; // arc flow + CNumber cost; // arc linear cost + + #if( QUADRATICCOST ) + CNumber quadraticCost; // arc quadratic cost + #else + char ident; // tells if arc is deleted, closed, in T, L, or U + #endif + + FNumber upper; // arc upper bound + }; // end( struct( arcPType ) ) + + struct arcDType { // arc structure for Primal Simplex - - - - - - - - + nodeDType *tail; // tail node + nodeDType *head; // head node + FNumber flow; // arc flow + CNumber cost; // arc linear cost + + #if( QUADRATICCOST ) + CNumber quadraticCost; // arc quadratic cost + #else + char ident; // indicates if arc is deleted, closed, in T, in L, or in U + #endif + + FNumber upper; // arc upper bound + arcDType *nextBs; // the next arc in the Backward Star of the arc's head + arcDType *nextFs; // the next arc in the Forward Star of the arc's tail + + }; // end( struct( arcDType ) ) + + struct primalCandidType { // Primal Candidate List- - - - - - - - - - - - - + arcPType *arc; // pointer to the violating primal bound arc + + #if(QUADRATICCOST ) + FONumber absRC; // absolute value of the arc's reduced cost + #else + CNumber absRC; // absolute value of the arc's reduced cost + #endif + }; // end( struct( primalCandidateType ) ) + + struct dualCandidType { // Dual Candidate List- - - - - - - - - - - - - - + nodeDType *node; // deepest node violating the dual bound arc + FNumber absInfeas; // absolute value of the arc's flow infeasibility + }; // end( struct( dualCandidateType ) ) + +/*--------------------------------------------------------------------------*/ +/*----------------------- PRIVATE DATA STRUCTURES -------------------------*/ +/*--------------------------------------------------------------------------*/ + + bool usePrimalSimplex; // TRUE if the Primal Network Simplex is used + + char pricingRule; // which pricing rule is used + + nodePType *nodesP; // vector of nodes: points to the n + 1 node + // structs (including the dummy root node) + // where the first node is indexed by zero + // and the last node is the dummy root node + + nodePType *dummyRootP; // the dummy root node + + nodePType *stopNodesP; // first infeasible node address = nodes + n + + arcPType *arcsP; // vector of arcs; this variable points to + // the m arc structs. + + arcPType *dummyArcsP; // vector of artificial dummy arcs: points + // to the artificial dummy arc variables and + // contains n arc structs. The artificial + // arcs are used to build artificial feasible + // starting bases. For each node i, there is + // exactly one dummy arc defined to connect + // the node i with the dummy root node. + + arcPType *stopArcsP; // first infeasible arc address = arcs + m + + arcPType *stopDummyP; // first infeasible dummy arc address + // = arcs + m + n + + arcPType *arcToStartP; // Dantzig Rule and First Eligible Arc Rule + // start their search from this arc + + nodeDType *nodesD; // vector of nodes: points to the n + 1 node + // structs (including the dummy root node) + // where the first node is indexed by zero + // and the last node is the dummy root node + + nodeDType *dummyRootD; // the dummy root node + + nodeDType *stopNodesD; // first infeasible node address = nodes + n + + arcDType *arcsD; // vector of arcs; this variable points to + // the m arc structs. + + arcDType *dummyArcsD; // vector of artificial dummy arcs: points to + // to the artificial dummy arc variables and + // contains n arc structs. The artificial + // arcs are used to build artificial feasible + // starting bases. For each node i, there is + // exactly one dummy arc defined to connect + // the node i with the dummy root node. + + arcDType *stopArcsD; // first infeasible arc address = arcs + m + + arcDType *stopDummyD; // first infeasible dummy arc address + // = arcs + m + n + + arcDType *arcToStartD; // Dantzig Rule and First Eligible Arc Rule + // start their search from this arc + + iteratorType iterator; // the current number of iterations + + primalCandidType *candP; // every element points to an element of the + // arcs vector which contains an arc violating + // dual bound + + dualCandidType *candD; // every element points to an element of the + // arcs vector which contains an arc violating + // primal bond + + Index numGroup; // number of the candidate lists + + Index tempCandidateListSize; // hot list dimension (it is variable) + + Index groupPos; // contains the actual candidate list + + Index numCandidateList; // number of candidate lists + + Index hotListSize; // number of candidate lists and hot list dimension + + Index forcedNumCandidateList; // value used to force the number of candidate list + + Index forcedHotListSize; // value used to force the number of candidate list + // and hot list dimension + + bool newSession; // true if algorithm is just started + + CNumber MAX_ART_COST; // large cost for artificial arcs + + FNumber *modifiedBalance; // vector of balance used by the PostVisit + + FONumber EpsOpt; // the precision of the objective function value + // for the quadratic case of the Primal Simplex + + int recomputeFOLimits; // after how many iterations the quadratic Primal + // Simplex computes "manually" the o.f. value + + #if( QUADRATICCOST ) + FONumber foValue; // the temporary objective function value + #endif + +/*--------------------------------------------------------------------------*/ +/*-------------------------- PRIVATE METHODS -------------------------------*/ +/*--------------------------------------------------------------------------*/ + + void MemAlloc( void ); + +/**< Method to allocate memory for the main data structures. It creates the + vector of nodes, the vector of arcs (real and dummy). If the algorithm is + using the Dual Simplex, it creates also the vectors whenInT2, firstIn and + nextIn, usefull to identify the next entering. */ + +/*--------------------------------------------------------------------------*/ + + void MemDeAlloc( bool whatDeAlloc ); + +/**< Method to deallocate memory for the main data structures created in + MemAlloc(). */ + +/*--------------------------------------------------------------------------*/ + + void MemAllocCandidateList( void ); + +/**< Method to allocate memory for the data structure used by the Candidate + List Pivot Rule. It creates the vector of candP (or candD in the Dual + Simplex case), determining the size of this vector on the basis of number + of arcs or nodes. */ + +/*--------------------------------------------------------------------------*/ + + void MemDeAllocCandidateList( void ); + +/**< Method to deallocate memory for the data structures created in + MemAllocCandidateList(). */ + +/*--------------------------------------------------------------------------*/ + + void CreateInitialPrimalBase( void ); + +/**< Method to create an initial feasible primal base. Add one node (dummyRoot) + to the network and connect this dummy root to each real node with n dummy + arcs. For each source node i, a dummy arc (i, r) exists. For each sink or + transit node j, a dummy arc (r, j) exists. The source nodes send their flows + to the dummy root through their dummy arcs, so the dummy root send all to + the sink nodes. The dummy root balance is 0, the costs of dummy arcs are + fixed to "infinity". */ + +/*--------------------------------------------------------------------------*/ + + void CreateInitialDualBase( void ); + +/**< Method to create an initial feasible dual base. Add one node (dummyRoot) + to the network and connect this dummy root to each real node with n dummy + arcs. The source nodes send their flows to the dummy root through their + dummy arcs, so the dummy root send all to the sink nodes. The dummy root + balance is 0, the costs of dummy arcs are fixed to "infinity". */ + +/*--------------------------------------------------------------------------*/ + + void CreateAdditionalDualStructures( void ); + +/**< The Dual Simplex needs nodes' Backward and Forward Star to work. So when + the Primal Simplex runs, these structures don't exist. When the Dual + Simplex starts, these structure are created in this method. */ + +/*--------------------------------------------------------------------------*/ + + void PrimalSimplex( void ); + +/**< Main method to implement the Primal Simplex algorithm. */ + +/*--------------------------------------------------------------------------*/ + + void DualSimplex( void ); + +/**< Main method to implement the Dual Simplex algorithm. */ + +/*--------------------------------------------------------------------------*/ + + template + void UpdateT( A *h , A *k , N *h1 , N *h2 , N *k1 , N *k2 ); + + /**< Method to update the spanning tree T every iteration. + The spanning tree T is implemented with a bidirectional list stored + in the node structure, which represents the nodes' order after a + Post-Visit. The parameter of the method are the outgoing arc "h", the + incoming arc "k", and the four nodes on the extremity of these arcs + (for example h2 is the deepest node of the outgoing arc). Removing the + arc "h" splits T in two subtrees: T1 (which contains the root of T) and + T2, which will be re-connected by the incoming arc "k". + T2 will be reordered; in fact, the node "k2" becomes the root of T2 + instead of "h2" and the hierarchy of T2 will be overturned. Then T2 will + be moved; the root of T2 is changed, therefore the predecessor of the + root will become the node "k1". + + This method uses the methods cutAndUpdateSubtree() and pasteSubtree(). + First it cuts the node "k2" and its subtree from T using + cutAndUpdateSubtree(). Moreover the method cutAndUpdateSubtree() + updates the field "subTreeLevel" of every subtree's nodes, since k2's + subtree will be moved from the bottom to the top of T2. Then the method + pasteSubtree() puts this subtree in the bidirectional list after the node + "k1". The same operations will be applied to the old precedessor of "k2" + (which will become one of the childs of "k2"). This second subtree will + be cut, the subTreeLevel fields will be updated, and it will be inserted + in the bidirectional list after the k2's subtree. This is iterated until + the node "h2" is reached. */ + +/*--------------------------------------------------------------------------*/ + + template + N* CutAndUpdateSubtree( N *root, int delta ); + +/**< This method cuts a generic subtree from the spanning tree T. Then it + updates the field "subTreeLevel" of every subtree's nodes adding the value + "delta". This method returns the last node of the subtree. */ + +/*--------------------------------------------------------------------------*/ + + template + void PasteSubtree( N *root , N *lastNode , N *previousNode ); + +/**< This method inserts a generic subtree with root passed by parameter into + the spanning tree T, between the nodes "previousNode" and "lastNode". */ + +/*--------------------------------------------------------------------------*/ + + arcPType* RuleDantzig( void ); + +/**< This method returns an arc which violates the dual conditions. It searchs + the arc with most violation of dual conditions in the entire set of real + arcs. It can be used only by the Primal Simplex in the case of networks + with linear costs. */ + +/*--------------------------------------------------------------------------*/ + + arcPType* PRuleFirstEligibleArc( void ); + +/**< This method returns the first found arc which violates the dual conditions + in the case of Primal Simplex, the primal condition in the case of Dual + Simplex. It can be used only in the case of networks with linear costs. */ + +/*--------------------------------------------------------------------------*/ + + arcDType* DRuleFirstEligibleArc( void ); + +/**< This method returns the first found arc which violates the dual conditions + in the case of Primal Simplex, the primal condition in the case of Dual + Simplex. It can be used only in the case of networks with linear costs. */ + +/*--------------------------------------------------------------------------*/ + + arcPType* RulePrimalCandidateListPivot( void ); + +/**< This method returns an arc which violates the dual conditions. It searches + the arc with most violation of dual conditions in a small set of candidate + arcs. In every iteration the method rebuilds this set of arcs executing three + phases: + + - in the first phase it analyzes the remaining arcs and delete the arcs which + don't violate the dual condition any more; + + - in the second phase it tries to fill the set, so it searchs other arcs + which violate the dual condition: the set of arcs is divided into "buckets" + which are searched sequentially until the candidate list is full; the + last visited bucket is retained, and the search is restarted from that + one at later iterations + + - in the third phase the small set of candidate arcs is ordered according + to the violation of dual condition by the method SortPrimalCandidateList() + using an implementation of the algorithm "quicksort". + + At last the method returns the first arc in the ordered small set. If the + arc doesn't exist (the set is empty), it returns NULL. */ + +/*--------------------------------------------------------------------------*/ + + inline void InitializePrimalCandidateList( void ); + +/**< Method to initialize some important details for Primal Candidate List + Rule. */ + +/*--------------------------------------------------------------------------*/ + + inline void SortPrimalCandidateList( Index min , Index max ); + +/**< Method to order the little set of candidate arcs according to + infeasibility of dual conditions. It implements the "quicksort" + algorithm. */ + +/*--------------------------------------------------------------------------*/ + + arcDType* RuleDualCandidateListPivot( void ); + +/**< Similar to RulePrimalCandidateListPivot() for the Dual Simplex. */ + +/*--------------------------------------------------------------------------*/ + + inline void InitializeDualCandidateList( void ); + +/**< Method to initialize some important details for Dual Candidate List Rule. + */ + +/*--------------------------------------------------------------------------*/ + + inline void SortDualCandidateList( Index min , Index max ); + +/**< Similar to SortPrimalCandidateList() for the Dual Simplex. */ + +/*--------------------------------------------------------------------------*/ + + template + inline void AddPotential( N *r , RCT delta ); + +/**< Method to quickly update the dual solutions. During the change of the + base, the potential of node "k2" (deepest node in T of incoming arc "k", + and new root of T2) changes according to the new structure of T. In fact, + the precedessor of "k2" changes: now the predecessor of "k2"is "k1" (the + other node of the incoming arc "k"). This change of predecessor causes a + change of potential of "k2". The change of potential of "k2" causes the + changes of potential of all nodes of T2. This method computes the change + of potential of "k2" and applies it on all the nodes of T2. */ + +/*--------------------------------------------------------------------------*/ + + template + inline void ComputePotential( N *r ); + +/**< Method to update the dual solutions. It computes all the potential of the + nodes of the subtree which has r as root. */ + +/*--------------------------------------------------------------------------*/ + + inline void ResetWhenInT2( void ); + +/**< Method to order the small set of candidate arcs according to dual + infeasibility. It implements the algorithm "quicksort". */ + +/*--------------------------------------------------------------------------*/ + + void CreateInitialPModifiedBalanceVector( void ); + +/**< Method to initialize the vector of modified balance for the postvisit on + T in the Primal Simplex data structure. */ + +/*--------------------------------------------------------------------------*/ + + void PostPVisit( nodePType *r ); + +/**< Method to calculate the flow on the basic arcs with the Primal Simplex's + data structure. It uses the set of the upper bound arcs, the construction + of a modified balance vector and the postvisit on T. */ + +/*--------------------------------------------------------------------------*/ + + void BalanceFlow( nodePType *r ); + +/**< This method works after the method PostPVisit (in a Primal context). It + restores primal admissimibility on the r's subtree. It starts from the leaf + of the subtree and goes up to the root, using the method AdjustFlow. */ + +/*--------------------------------------------------------------------------*/ + + void AdjustFlow( nodePType *r ); + +/**< This method checks the primal admissibility of the r's basic entering arc. + If it is out of bounds, the method removes it from the tree (and keeps the + relative dummy arc) and push flow in the cycle (some tree's arc and the old + entering arc) to restores the right balances of the node. */ + +/*--------------------------------------------------------------------------*/ + + void CreateInitialDModifiedBalanceVector( void ); + +/**< Method to initialize the vector of modified balance for the postvisit on + T in the Dual Simplex data structure. */ + +/*--------------------------------------------------------------------------*/ + + void PostDVisit( nodeDType *r ); + +/**< Method to calculate the flow on the basic arcs with the Dual Simplex's data + structure, using the set of the upper bound arcs, the construction of a + modified balance vector and the postvisit on T. */ + +/*--------------------------------------------------------------------------*/ + + template + inline N* Father( N *n, A *a ); + +/**< Method to find the predecessor of the node in the tree. */ + +/*--------------------------------------------------------------------------*/ + + #if(QUADRATICCOST) + template + inline FONumber ReductCost( A *a ); + #else + template + inline CNumber ReductCost( A *a ); + #endif + +/**< Method to calculate the reduct cost of the arc. */ + +/*--------------------------------------------------------------------------*/ + + inline FONumber GetFO( void ); + +/**< Method to calculate the temporary (or the final) objective function + value. */ + +/*--------------------------------------------------------------------------*/ + + void PrintPNode( nodePType *nodo ); + +/**< Method to print the "name" of the node in the Primal Simplex. */ + +/*--------------------------------------------------------------------------*/ + + void PrintPArc( arcPType *arc ); + +/**< Method to print the "name" of the arc in the Primal Simplex. */ + +/*--------------------------------------------------------------------------*/ + + void PrintDNode( nodeDType *nodo ); + +/**< Method to print the "name" of the node in the Dual Simplex. */ + +/*--------------------------------------------------------------------------*/ + + void PrintDArc( arcDType *arc ); + +/**< Method to print the "name" of the arc in the Dual Simplex. */ + +/*--------------------------------------------------------------------------*/ + + nodePType* RecoverPNode( Index ind ); + +/**< Method to find a node (in the Primal Simplex) using its index. */ + +/*--------------------------------------------------------------------------*/ + + arcPType* RecoverPArc( nodePType *tail , nodePType *head ); + +/**< Method to find an arc (in the Primal Simplex) using 2 pointers to tail + node and head node. */ + +/*--------------------------------------------------------------------------*/ + + nodeDType* RecoverDNode( Index ind ); + +/**< Method to find a node (in the Dual Simplex) using its index. */ + +/*--------------------------------------------------------------------------*/ + + arcDType* RecoverDArc( nodeDType *tail , nodeDType *head ); + +/**< Method to find an arc (in the Dual Simplex) using 2 pointers to tail + node and head node. */ + +/*--------------------------------------------------------------------------*/ + + void infoPNode( nodePType *node , int tab ); + +/**< Method to print some information of the node (in the Primal Simplex). */ + +/*--------------------------------------------------------------------------*/ + + void infoPArc( arcPType *arc , int ind , int tab ); + +/**< Method to print some information of the arc (in the Primal Simplex). */ + +/*--------------------------------------------------------------------------*/ + + void infoDNode( nodeDType *node , int tab ); + +/**< Method to print some information of the node (in the Dual Simplex). */ + +/*--------------------------------------------------------------------------*/ + + void infoDArc( arcDType *arc , int ind , int tab ); + +/**< Method to print some information of the arc (in the Dual Simplex). */ + +/*--------------------------------------------------------------------------*/ + + void ShowSituation( int tab ); + +/**< Method to show the actual complete situation. */ + +/*--------------------------------------------------------------------------*/ + + }; // end( class MCFSimplex ) + +/* @} end( group( MCFSimplex_CLASSES ) ) */ + +#endif /* MCFSimplex.h included */ + +/*-------------------------------------------------------------------------*/ +/*-------------------inline methods implementation-------------------------*/ +/*-------------------------------------------------------------------------*/ + +inline MCFClass::Index MCFSimplex::MCFSNde( MCFClass::cIndex i ) +{ + if( usePrimalSimplex ) + return( Index( ( (arcsP + i)->tail - nodesP + 1 ) - USENAME0 ) ); + else + return( Index( ( (arcsD + i)->tail - nodesD + 1 ) - USENAME0 ) ); + } + +/*-------------------------------------------------------------------------*/ + +inline MCFClass::Index MCFSimplex::MCFENde( MCFClass::cIndex i ) +{ + if( usePrimalSimplex ) + return( Index( ( (arcsP + i)->head - nodesP + 1 ) - USENAME0 ) ); + else + return( Index( ( (arcsD + i)->head - nodesD + 1 ) - USENAME0 ) ); + } + +/*-------------------------------------------------------------------------*/ + +inline MCFClass::CNumber MCFSimplex::MCFCost( MCFClass::cIndex i ) +{ + if( usePrimalSimplex ) + return( (arcsP + i)->cost ); + else + return( (arcsD + i)->cost ); + } + +/*-------------------------------------------------------------------------*/ + +inline MCFClass::CNumber MCFSimplex::MCFQCoef( MCFClass::cIndex i ) +{ + #if( QUADRATICCOST ) + if( usePrimalSimplex ) + return( (arcsP + i)->quadraticCost ); + else + return( (arcsD + i)->quadraticCost ); + #else + return( 0 ); + #endif + } + +/*-------------------------------------------------------------------------*/ + +inline MCFClass::FNumber MCFSimplex::MCFUCap( MCFClass::cIndex i ) +{ + if( usePrimalSimplex ) + return( (arcsP + i)->upper ); + else + return( (arcsD + i)->upper ); + } + +/*-------------------------------------------------------------------------*/ + +inline MCFClass::FNumber MCFSimplex::MCFDfct( MCFClass::cIndex i ) +{ + if( usePrimalSimplex ) + return( (nodesP + i)->balance ); + else + return( (nodesD + i)->balance ); + } + +/*-------------------------------------------------------------------------*/ + +#if( QUADRATICCOST ) + +template +inline MCFSimplex::FONumber MCFSimplex::ReductCost( A *a ) +{ + FONumber redc = (a->tail)->potential - (a->head)->potential; + redc = redc + a->cost; + redc = redc + a->quadraticCost * a->flow; + return( redc ); + } + +#else + +template +inline MCFSimplex::CNumber MCFSimplex::ReductCost( A *a ) +{ + CNumber redc = (a->tail)->potential - (a->head)->potential; + redc = redc + a->cost; + return( redc ); + } + +#endif + +/*-------------------------------------------------------------------------*/ + +#if( OPT_USE_NAMESPACES ) +}; // end( namespace MCFClass_di_unipi_it ) +#endif + +/*-------------------------------------------------------------------------*/ +/*-------------------------------------------------------------------------*/ + +/*-------------------------------------------------------------------------*/ +/*---------------------- End File MCFSimplex.h ----------------------------*/ +/*-------------------------------------------------------------------------*/ diff --git a/impl/MCFSimplex.o b/impl/MCFSimplex.o new file mode 100644 index 0000000..f9378b6 Binary files /dev/null and b/impl/MCFSimplex.o differ diff --git a/impl/Makefile b/impl/Makefile index ca0b37d..492528d 100644 --- a/impl/Makefile +++ b/impl/Makefile @@ -2,7 +2,8 @@ CC=gcc CPP=g++ BUILD=build PARSER=parser -FLAGS=-Wall -Werror -Wextra -pedantic -lantlr3c -fno-exceptions -lrt +FLAGS= -lantlr3c -fno-exceptions -lrt +#-Wall -Werror -Wextra -pedantic -lantlr3c -fno-exceptions -lrt NORMAL_FLAGS=$(FLAGS) -g -pg OPTIMISED_FLAGS=$(FLAGS) -O3 @@ -16,16 +17,19 @@ check-syntax: $(PARSER) -$(CPP) $(FLAGS) -o /tmp/null -x c++ $(CHK_SOURCES) # the - is to ignore the return code. -$(BUILD)/main: $(BUILD) $(PARSER) main.cpp *.hpp - $(CPP) main.cpp $(PARSER)/*.o -o $(BUILD)/main $(NORMAL_FLAGS) +$(BUILD)/main: $(BUILD) $(PARSER) main.o MCFSimplex.o + $(CPP) main.o MCFSimplex.o $(PARSER)/*.o -o $(BUILD)/main $(NORMAL_FLAGS) -$(BUILD)/fast: $(BUILD) $(PARSER) main.cpp *.hpp - $(CPP) main.cpp $(PARSER)/*.o -o $(BUILD)/fast $(OPTIMISED_FLAGS) +main.o: main.cpp *.hpp *.h + $(CPP) -c main.cpp $(NORMAL_FLAGS) + +MCFSimplex.o: MCFSimplex.cpp *.h + $(CPP) -c MCFSimplex.cpp $(NORMAL_FLAGS) $(PARSER): EquationSystem.g mkdir -p $(PARSER) java -jar ../antlr/antlr-3.4-complete.jar -o $(PARSER) EquationSystem.g - cd $(PARSER); $(CC) *.c -c -lantrl3c + cd $(PARSER); $(CC) *.c -c -lantlr3c $(BUILD): mkdir -p $(BUILD) @@ -37,3 +41,4 @@ test: $(BUILD)/main clean: rm -rf $(BUILD) rm -rf $(PARSER) + rm -rf *.o diff --git a/impl/OPTUtils.h b/impl/OPTUtils.h new file mode 100755 index 0000000..66ad4af --- /dev/null +++ b/impl/OPTUtils.h @@ -0,0 +1,475 @@ +/*--------------------------------------------------------------------------*/ +/*--------------------------- File OPTutils.h ------------------------------*/ +/*--------------------------------------------------------------------------*/ +/** @file + * Small classes are provided for: + * - reading the time of a code; + * - generating random numbers. + * + * The classes can be adapted to different environments setting a + * compile-time switch in this file. + * + * Additionally, a function is provided for safely reading parameters + * out of a stream. + * + * \version 1.00 + * + * \date 04 - 10 - 2010 + * + * \author Antonio Frangioni \n + * Operations Research Group \n + * Dipartimento di Informatica \n + * Universita' di Pisa \n + * + * Copyright © 1994 - 2010 by Antonio Frangioni + */ +/*--------------------------------------------------------------------------*/ +/*--------------------------------------------------------------------------*/ +/*--------------------------------------------------------------------------*/ + +#ifndef __OPTutils + #define __OPTutils /* self-identification: #endif at the end of the file */ + +/*--------------------------------------------------------------------------*/ +/*----------------------------- MACROS -------------------------------------*/ +/*--------------------------------------------------------------------------*/ +/** @defgroup OPTUTILS_MACROS Compile-time switches in OPTutils.h + These macros control how the classes OPTTimers and OPTrand are + implemented; choose the appropriate value for your environment, + or program a new version if no value suits you. + Also, namespaces can be eliminated if they create problems. + @{ */ + + +/*----------------------- OPT_USE_NAMESPACES -------------------------------*/ + +#define OPT_USE_NAMESPACES 0 + +/**< Setting OPT_USE_NAMESPACES == 0 should instruct all codes that use + OPTutils stuff to avoid using namespaces; to start with, the common + namespace OPTutils_di_unipi_it, that contains all the types defined + herein, is *not* defined. */ + +/*---------------------------- OPT_TIMERS ----------------------------------*/ + +#define OPT_TIMERS 5 + +/**< The class OPTtimers is defined below to give an abstract interface to the + different timing routines that are used in different platforms. This is + needed since time-related functions are one of the less standard parts of + the C[++] library. The value of the OPT_TIMERS constant selects among the + different timing routines: + + - 1 = Use the Unix times() routine in sys/times.h + + - 2 = As 1 but uses sys/timeb.h (typical for Microsoft(TM) compilers) + + - 3 = Still use times() of sys/times.h, but return wallclock time + rather than CPU time + + - 4 = As 3 but uses sys/timeb.h (typical for Microsoft(TM) compilers) + + - 5 = return the user time obtained with ANSI C clock() function; this + may result in more accurate running times w.r.t. but may be limited + to ~ 72 hours on systems where ints are 32bits. + + - 6 = Use the Unix gettimeofday() routine of sys/time.h. + + Any unsupported value would simply make the class to report constant + zero as the time. + + The values 1 .. 4 rely on the constant CLK_TCK for converting between + clock ticks and seconds; for the case where the constant is not defined by + the compiler -- should not happen, but it does -- or it is defined in a + wrong way, the constant is re-defined below. */ + +/*---------------------------- OPT_RANDOM ---------------------------------*/ + +#define OPT_RANDOM 1 + +/**< The class OPTrand is defined below to give an abstract interface to the + different random generators that are used in different platforms. This is + needed since random generators are one of the less standard parts of the + C[++] library. The value of the OPT_RANDOM constant selects among the + different timing routines: + + - 0 = an hand-made implementation of a rather good random number generator + is used; note that this assumes that long ints >= 32 bits + + - 1 = standard rand() / srand() pair, common to all C libreries but not + very sophisticated + + - 2 = drand48() / srand48(), common on Unix architectures and pretty good. + + Any unsupported value would simply make the functions to report constant + zero, which is not nice but useful to quickly fix problems if you don't + use random numbers at all. */ + +/*@} -----------------------------------------------------------------------*/ +/*------------------------------ INCLUDES ----------------------------------*/ +/*--------------------------------------------------------------------------*/ + +#if( OPT_RANDOM ) + #include + + /* For random routines, see OPTrand() below. */ +#endif + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ + +#if( OPT_TIMERS <= 4 ) + #if( ( OPT_TIMERS == 1 ) || ( OPT_TIMERS == 3 ) ) + #include + #else + #include + #endif + #include +#elif( OPT_TIMERS == 5 ) + #include +#elif( OPT_TIMERS == 6 ) + #include +#endif + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ + +#include +#include + +/* For istream and the >> operator, used in DfltdSfInpt(). */ + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ + +/*--------------------------------------------------------------------------*/ +/*--------------------------- NAMESPACE ------------------------------------*/ +/*--------------------------------------------------------------------------*/ + +#if( OPT_USE_NAMESPACES ) +namespace OPTtypes_di_unipi_it +{ + /** @namespace OPTtypes_di_unipi_it + The namespace OPTtypes_di_unipi_it is defined to hold all the data + types, constants, classes and functions defined here. It also + comprises the namespace std. */ +#endif + + using namespace std; // I know it's not elegant, but ... + +/*--------------------------------------------------------------------------*/ +/*----------------------------- NULL ---------------------------------------*/ +/*--------------------------------------------------------------------------*/ + +#ifndef NULL + #define NULL 0 +#endif + +/* Curiously enough, some compilers do not always define NULL; for instance, + sometimes it is defined in stdlib.h. */ + +/*--------------------------------------------------------------------------*/ +/*---------------------------- CLK_TCK -------------------------------------*/ +/*--------------------------------------------------------------------------*/ + +#ifndef CLK_TCK + #define CLK_TCK 100 +#endif + +/* CLK_TCK is the constant used to transform (integer) time values in seconds + for times()-based routines. Its normal value is 100. */ + +/*--------------------------------------------------------------------------*/ +/*--------------------------- OPT_TIMERS -----------------------------------*/ +/*--------------------------------------------------------------------------*/ +/** @defgroup OPTtypes_CLASSES Classes in OPTutils.h + @{ */ + +#if( OPT_TIMERS ) + +/** Provides a common interface to the different timing routines that are + available in different platforms. */ + +class OPTtimers { + + public: //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + /// constructor of the class + OPTtimers( void ) + { + ReSet(); + } + + /// start the timer + void Start( void ) + { + if( ! ticking ) { + #if( ( OPT_TIMERS == 1 ) || ( OPT_TIMERS == 2 ) ) + times( &buff ); + t_u = buff.tms_utime; + t_s = buff.tms_stime; + #elif( ( OPT_TIMERS == 3 ) || ( OPT_TIMERS == 4 ) ) + t_u = times( &buff ); + #elif( OPT_TIMERS == 5 ) + t_u = clock(); + #elif( OPT_TIMERS == 6 ) + struct timeval t; + gettimeofday( &t , NULL ); + t_u = double( t.tv_sec + t.tv_usec * 1e-6 ); + #endif + + ticking = 1; + } + } + + /// stop the timer + void Stop( void ) + { + if( ticking ) { + Read( u , s ); + ticking = 0; + } + } + + /** Return the elapsed time. If the clock is ticking, return the *total* + time since the last Start() without stopping the clock; otherwise, + return the total elapsed time of all the past runs of the clock since + the last ReSet() [see below]. */ + + double Read( void ) + { + double tu = 0; + double ts = 0; + Read( tu , ts ); + return( tu + ts ); + } + + /// As Read( void ) but *adds* user and system time to tu and ts. + void Read( double &tu , double &ts ) + { + if( ticking ) { + #if( ( OPT_TIMERS == 1 ) || ( OPT_TIMERS == 2 ) ) + times( &buff ); + tu += ( double( buff.tms_utime - t_u ) ) / double( CLK_TCK ); + ts += ( double( buff.tms_stime - t_s ) ) / double( CLK_TCK ); + #elif( ( OPT_TIMERS == 3 ) || ( OPT_TIMERS == 4 ) ) + tu += ( double( times( &buff ) - t_u ) ) / double( CLK_TCK ); + #elif( OPT_TIMERS == 5 ) + tu += ( double( clock() - t_u ) ) / double( CLOCKS_PER_SEC ); + #elif( OPT_TIMERS == 6 ) + struct timeval t; + gettimeofday( &t , NULL ); + tu += double( t.tv_sec + t.tv_usec * 1e-6 ) - t_u; + #endif + } + else { tu += u; ts += s; } + } + + /// reset the timer + void ReSet( void ) + { + u = s = 0; ticking = 0; + } + + private: //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + double u; // elapsed *user* time, in seconds + double s; // elapsed *system* time, in seconds + char ticking; // 1 if the clock is ticking + + #if( ( OPT_TIMERS > 0 ) && ( OPT_TIMERS <= 5 ) ) + clock_t t_u; + + #if( OPT_TIMERS <= 4 ) + struct tms buff; + + #if( ( OPT_TIMERS == 1 ) || ( OPT_TIMERS == 3 ) ) + clock_t t_s; + #endif + #endif + #elif( OPT_TIMERS == 6 ) + double t_u; + #endif + + }; // end( class OPTtimers ); + +#endif + +/*--------------------------------------------------------------------------*/ +/*------------------------------ OPTrand() ---------------------------------*/ +/*--------------------------------------------------------------------------*/ + +/** Provide a common interface to the different random generators that are + available in different platforms. */ + +class OPTrand { + + public: //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + /// constructor of the class + OPTrand( void ) + { + #if( OPT_RANDOM == 0 ) + A[ 0 ] = -1; + srand( long( 123456789 ) ); + #else + OPTrand::srand( long( 1 ) ); + #endif + } + + /** Returns a random number uniformly distributed in [0, 1). + \note each object of class OPTrand has its own sequence, so that + multiple OPTrand objects being used within the same program do not + interfere with each other (as opposed to what C random routines + would do). */ + + double rand( void ) + { + #if( OPT_RANDOM == 0 ) + long nmbr = *(gb_fptr--); + if( nmbr < 0 ) + nmbr = gb_flip_cycle(); + return( double( nmbr ) / double( (unsigned long)0x80000000 ) ); + #elif( OPT_RANDOM == 1 ) + ::srand( myseed ); + myseed = ::rand(); + return( double( myseed ) / double( RAND_MAX ) ); + #elif( OPT_RANDOM == 2 ) + return( erand48( myseed ) ); + #else + return( 0 ); // random, eh? + #endif + } + + /// Seeds the random generator for this instance of OPTrand. + void srand( long seed ) + { + #if( OPT_RANDOM == 0 ) + long prev = seed , next = 1; + seed = prev = mod_diff( prev , 0 ); + A[ 55 ] = prev; + + for( long i = 21 ; i ; i = ( i + 21 ) % 55 ) { + A[ i ] = next; + next = mod_diff( prev , next ); + if( seed & 1 ) + seed = 0x40000000 + ( seed >> 1 ); + else + seed >>= 1; + + next = mod_diff( next , seed ); + prev = A[ i ]; + } + + gb_flip_cycle(); + gb_flip_cycle(); + gb_flip_cycle(); + gb_flip_cycle(); + gb_flip_cycle(); + #elif( OPT_RANDOM == 1 ) + myseed = int( seed ); + #elif( OPT_RANDOM == 2 ) + long *sp = (long*)( &myseed ); + *sp = seed; // copy higher 32 bits + myseed[ 2 ] = 0x330E; // initialize lower 16 bits + #endif + } + + private: //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + #if( OPT_RANDOM == 0 ) + long A[ 56 ]; + long *gb_fptr; + + long mod_diff( long x , long y ) + { + return( ( ( x ) - ( y ) ) & 0x7fffffff ); + } + + long gb_flip_cycle( void ) + { + long *ii, *jj; + for( ii = &A[ 1 ] , jj = &A[ 32 ] ; jj <= &A[ 55 ] ; ii++ , jj++ ) + *ii = mod_diff( *ii , *jj ); + + for( jj = &A[ 1 ] ; ii <= &A[ 55 ] ; ii++ , jj++ ) + *ii = mod_diff( *ii , *jj ); + + gb_fptr = &A[ 54 ]; + + return A[ 55 ]; + } + #elif( OPT_RANDOM == 1 ) + int myseed; + #elif( OPT_RANDOM == 2 ) + unsigned short int myseed[ 3 ]; + #endif + + }; // end( class( OPTrand ) ) + +/* @} end( group( OPTtypes_CLASSES ) ) */ +/*--------------------------------------------------------------------------*/ +/*--------------------------- DfltdSfInpt() --------------------------------*/ +/*--------------------------------------------------------------------------*/ +/** @defgroup OPTtypes_FUNCTIONS Functions in OPTutils.h + @{ */ + +/** Template function for reading parameters from a istream. The function is + "safe" because it works also if the istream is not given, is not be long + enough or contains erroneous things. + + Given a &istream (possibly NULL), DfltdSfInpt() attempts to read Param out + of it, skipping any line that begins with the comment carachter (defaulted + to '#'), any blank line and any line starting with anything that can not + be interpreted as a `T'. If, for any reason, the read operation fails, + then the parameter is given the default value `Dflt'. Otherwise, all the + rest of the line up to the nearest newline ('\n') carachter is flushed. */ + +template +inline void DfltdSfInpt( istream *iStrm , T &Param , const T Dflt , + const char cmntc = '#' ) +{ + string comm( 1 , cmntc ); + + // the "! ! stream" trick is there to force the compiler to apply the + // stream -> bool conversion, in case it is too dumb to do it by itself + if( iStrm && ( ! ( ! (*iStrm) ) ) ) { + string buf; + + for( ;; ) { + if( ! ( (*iStrm) >> ws ) ) // skip whitespace + break; + + if( ! (*iStrm).good() ) // check stream is OK + break; + + getline( *iStrm , buf ); + + if( ! buf.empty() ) + if( buf.substr( 0 , 1 ).compare( comm ) ) { + stringstream ss; + ss << buf; + if( ! ( ss >> Param ) ) + break; + + return; + } + } + } + + Param = Dflt; + + } // end( DfltdSfInpt ) + +/* @} end( group( OPTtypes_FUNCTIONS ) ) */ +/*--------------------------------------------------------------------------*/ + +#if( OPT_USE_NAMESPACES ) + }; // end( namespace OPTtypes_di_unipi_it ) +#endif + +/*--------------------------------------------------------------------------*/ +/*--------------------------------------------------------------------------*/ + +#endif /* OPTutils.h included */ + +/*--------------------------------------------------------------------------*/ +/*---------------------- End File OPTutils.h -------------------------------*/ +/*--------------------------------------------------------------------------*/ diff --git a/impl/Operator.hpp b/impl/Operator.hpp index 64ef096..4a573b8 100644 --- a/impl/Operator.hpp +++ b/impl/Operator.hpp @@ -135,6 +135,88 @@ struct Guard : public Operator { } }; +#include "MCFSimplex.h" + +template +struct MinCostFlow : public Operator { + MinCostFlow(const std::vector& supplies, const std::vector > arcs) + : _supplies(supplies), _arcs(arcs), _solver(0,0) { + MCFClass::FNumber* deficits = new MCFClass::FNumber[supplies.size()]; + MCFClass::Index* starts = new MCFClass::Index[arcs.size()]; + MCFClass::Index* ends = new MCFClass::Index[arcs.size()]; + + for (int i = 0, size = supplies.size(); i < size; ++i) { + deficits[i] = -supplies[i].template as(); + } + for (int i = 0, size = arcs.size(); i < size; ++i) { + starts[i] = arcs[i].first; + ends[i] = arcs[i].second; + } + + _solver.LoadNet(supplies.size(), supplies.size(), // max nodes/arcs + supplies.size(), supplies.size(), // current nodes/arcs + NULL, NULL, // arcs have inf cap, zero cost (to begin) + deficits, // deficits for each node + starts, ends); // start/end of each edge + + delete[] deficits; + delete[] starts; + delete[] ends; + } + Domain eval (const std::vector& costs) const { + assert(costs.size() == _arcs.size()); + if (costs.size() == 0) + return 0; + for (int i = 0, size = costs.size(); i < size; ++i) { + _solver.ChgCost(i, costs[i].template as()); + } + _solver.SolveMCF(); + if (_solver.MCFGetStatus() == MCFClass::kUnfeasible){ + return -infinity(); + } else if (_solver.MCFGetFO() == MCFClass::Inf()) { + return infinity(); + } else if (_solver.MCFGetFO() == -MCFClass::Inf()) { + return -infinity(); + } else { + return _solver.MCFGetFO(); + } + } + void print(std::ostream& cout) const { + std::string supplyString = "["; + for (int i = 0, size = _supplies.size(); i < size; ++i) { + if (i > 0) + supplyString += ","; + std::stringstream stream; + stream << _supplies[i]; + supplyString += stream.str(); + } + supplyString += ']'; + + std::string arcString = "["; + for (int i = 0, size = _arcs.size(); i < size; ++i) { + if (i > 0) + arcString += ","; + { + std::stringstream stream; + stream << _arcs[i].first; + arcString += stream.str() + ":"; + } + { + std::stringstream stream; + stream << _arcs[i].second; + arcString += stream.str(); + } + } + arcString += ']'; + + cout << "MCF<" << supplyString << "," << arcString << ">"; + } +private: + std::vector _supplies; + std::vector > _arcs; + mutable MCFSimplex _solver; +}; + /*#include "TemplateConstraintMatrix.hpp" template diff --git a/impl/main.cpp b/impl/main.cpp index be9cc82..0daa72a 100644 --- a/impl/main.cpp +++ b/impl/main.cpp @@ -19,6 +19,59 @@ extern "C" { using namespace std; +template +vector toSupplies(pANTLR3_BASE_TREE node) { + ANTLR3_UINT32 num = node->getChildCount(node); + T output; + vector supplies; + pANTLR3_BASE_TREE childNode; + + for (int i = 0; i < num; ++i) { + childNode = (pANTLR3_BASE_TREE) node->getChild(node, i); + stringstream stream((char*)childNode->getText(childNode)->chars); + assert(stream >> output); + supplies.push_back(output); + } + + return supplies; +} + +pair toArc(pANTLR3_BASE_TREE node) { + pair result; + int output; + pANTLR3_BASE_TREE childNode; + + ANTLR3_UINT32 num = node->getChildCount(node); + assert(num == 2); + + { + childNode = (pANTLR3_BASE_TREE) node->getChild(node, 0); + stringstream stream((char*)childNode->getText(childNode)->chars); + assert(stream >> output); + result.first = output; + } + + { + childNode = (pANTLR3_BASE_TREE) node->getChild(node, 1); + stringstream stream((char*)childNode->getText(childNode)->chars); + assert(stream >> output); + result.second = output; + } + + return result; +} + +vector > toArcs(pANTLR3_BASE_TREE node) { + vector > arcs; + ANTLR3_UINT32 num = node->getChildCount(node); + pANTLR3_BASE_TREE childNode; + for (int i = 0; i < num; ++i) { + childNode = (pANTLR3_BASE_TREE) node->getChild(node, i); + arcs.push_back(toArc(childNode)); + } + return arcs; +} + template Expression& treeToExpression(pANTLR3_BASE_TREE node, EquationSystem& system) { ANTLR3_UINT32 num = node->getChildCount(node); @@ -39,7 +92,23 @@ Expression& treeToExpression(pANTLR3_BASE_TREE node, EquationSystem& syste } } - // anything that's not a constant/variable + if (name == "MCF") { + pANTLR3_BASE_TREE childNode; + + childNode = (pANTLR3_BASE_TREE) node->getChild(node, 0); + vector supplies = toSupplies(childNode); + childNode = (pANTLR3_BASE_TREE) node->getChild(node, 1); + vector > arcs = toArcs(childNode); + + vector*> args; + for (unsigned int i = 2; i < num; ++i) { + childNode = (pANTLR3_BASE_TREE) node->getChild(node, i); + args.push_back(&treeToExpression(childNode, system)); + } + return system.expression(new MinCostFlow(supplies, arcs), args); + } + + // anything that's not a constant/variable, or an MCF expr vector*> args; pANTLR3_BASE_TREE childNode; for (unsigned int i = 0; i < num; ++i) { @@ -70,7 +139,7 @@ Expression& treeToExpression(pANTLR3_BASE_TREE node, EquationSystem& syste } else if (name == "GUARD" || name == "guard") { op = new Guard(); } else { - std::cout << "throw exception" << *(char*)NULL; + assert(false); //throw "Parse error: Unknown operator"; } return system.expression(op, args); -- cgit v1.2.3