diff options
Diffstat (limited to 'impl')
-rw-r--r-- | impl/Complete.hpp | 10 | ||||
-rw-r--r-- | impl/EquationSystem.g | 18 | ||||
-rwxr-xr-x | impl/MCFClass.h | 2438 | ||||
-rw-r--r-- | impl/MCFSimplex.cpp | 4198 | ||||
-rw-r--r-- | impl/MCFSimplex.h | 1086 | ||||
-rw-r--r-- | impl/MCFSimplex.o | bin | 0 -> 205412 bytes | |||
-rw-r--r-- | impl/Makefile | 17 | ||||
-rwxr-xr-x | impl/OPTUtils.h | 475 | ||||
-rw-r--r-- | impl/Operator.hpp | 82 | ||||
-rw-r--r-- | impl/main.cpp | 73 |
10 files changed, 8383 insertions, 14 deletions
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<typename Z> friend std::ostream& operator<<(std::ostream&, const Complete<Z>&); + template<typename S> + S as() const { + if (_infinity) { + if (_value > 0) + return infinity<S>(); + return -infinity<S>(); + } + 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 <iomanip> +#include <sstream> +#include <limits> +#include <cassert> + +// 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<type>(). */ + + template <typename T> + class Inf { + public: + Inf() {} + operator T() { return( std::numeric_limits<T>::max() ); } + }; + +/*--------------------------------------------------------------------------*/ +/** Very small class to simplify extracting the "machine epsilon" for a + basic type (FNumber, CNumber); just use Eps<type>(). */ + + template <typename T> + class Eps { + public: + Eps() {} + operator T() { return( std::numeric_limits<T>::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<FNumber>() * 100; + EpsCst = Eps<CNumber>() * 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 <number of nodes> <number of arcs> + + Then the node definition lines must be found, in the form + + n <node number> <node supply> + + 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 <start node> <end node> <lower bound> <upper bound> <flow cost> + + There must be exactly <number of arcs> 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, <quadratic cost>. 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<Index>() ) = 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<Index>()-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<Index>() ) = 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<Index>()-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<Index>() ) = 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<Index>()-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<FONumber>() if MCFGetStatus() == kUnfeasible and + - Inf<FONumber>() 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<FONumber>() ); + 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<Index>(); + 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<Index>()-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<Index>() ); + } + +/**< 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<Index>() 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<Index>() ) = 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<Index>()-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<Index>() ) = 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<Index>()-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<Index>() ) + +/**< 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<Index>()-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<Index>() ) = 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<Index>()-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<Index>() ) = 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<Index>()-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<Index>() ) = 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<Index>()-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<Index>() ) + +/**< 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<Index>()-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<Index>() ) = 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<Index>()-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<Index>() ) = 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<Index>()-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<Index>() 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<Index>() , + cIndex nEN = Inf<Index>() ) = 0; + +/**< Change the starting and/or ending node of arc `name' to nSN and nEN. + Each parameter being Inf<Index>() 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<Index>() 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<class T> + inline bool ETZ( T x , const T eps ) + { + if( numeric_limits<T>::is_integer ) + return( x == 0 ); + else + return( (x <= eps ) && ( x >= -eps ) ); + } + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ +/** true if flow x is greater than zero (possibly considering tolerances). */ + + template<class T> + inline bool GTZ( T x , const T eps ) + { + if( numeric_limits<T>::is_integer ) + return( x > 0 ); + else + return( x > eps ); + } + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ +/** true if flow x is greater than or equal to zero (possibly considering + tolerances). */ + + template<class T> + inline bool GEZ( T x , const T eps ) + { + if( numeric_limits<T>::is_integer ) + return( x >= 0 ); + else + return( x >= - eps ); + } + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ +/** true if flow x is less than zero (possibly considering tolerances). */ + + template<class T> + inline bool LTZ( T x , const T eps ) + { + if( numeric_limits<T>::is_integer ) + return( x < 0 ); + else + return( x < - eps ); + } + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ +/** true if flow x is less than or equal to zero (possibly considering + tolerances). */ + + template<class T> + inline bool LEZ( T x , const T eps ) + { + if( numeric_limits<T>::is_integer ) + return( x <= 0 ); + else + return( x <= eps ); + } + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ +/** true if flow x is greater than flow y (possibly considering tolerances). + */ + + template<class T> + inline bool GT( T x , T y , const T eps ) + { + if( numeric_limits<T>::is_integer ) + return( x > y ); + else + return( x > y + eps ); + } + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ +/** true if flow x is less than flow y (possibly considering tolerances). */ + + template<class T> + inline bool LT( T x , T y , const T eps ) + { + if( numeric_limits<T>::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<FNumber>::is_integer ) || + ( ! numeric_limits<CNumber>::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 <algorithm> +#include <iostream> + +#include <cstdlib> +#include <ctime> + +/*--------------------------------------------------------------------------*/ +/*-------------------------------- 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<class T> +inline T ABS( const T x ) +{ + return( x >= T( 0 ) ? x : - x ); + } + +/*--------------------------------------------------------------------------*/ + +template<class T> +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<FNumber>::is_integer ) + throw( MCFException( "FNumber must be float if QUADRATICCOST == 1" ) ); + + if( numeric_limits<CNumber>::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<CNumber>::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<FNumber>(); + 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<FNumber>(); + 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<Index>(); + } + 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<FONumber>() ); + else + return( Inf<FONumber>() ); + + } // 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<CNumber>() ); + #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<CNumber>() ) + return; + + arc->cost = Inf<CNumber>(); + #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<CNumber>(); + #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<CNumber>(); + #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<Index>() ); + + n++; + if( usePrimalSimplex ) { + nodePType *newNode = nodesP + n - 1; + stopArcsP->tail = newNode; + stopArcsP->head = dummyRootP; + stopArcsP->upper = Inf<FNumber>(); + stopArcsP->flow = 0; + stopArcsP->cost = Inf<CNumber>(); + #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<CNumber>(); + 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<FNumber>() ) + return; + + if( arc->cost < Inf<CNumber>() ) + #else + if( arc->cost == DELETED ) + return; + + if( arc->cost >= BASIC ) + #endif + CloseArc( name ); + + #if( QUADRATICCOST ) + arc->upper = -Inf<FNumber>(); + #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<FNumber>() ) && ( arc != stopArcsP ) ) + #else + while( ( arc->ident > DELETED ) && ( arc != stopArcsP ) ) + #endif + arc++; + + if( arc == stopArcsP ) { + if( m >= mmax ) + return( Inf<Index>() ); + + 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<Index>() ); + + 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<FNumber>() ) ); + #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<FNumber>(); + } + + 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<FNumber>(); // 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<FNumber>() ) { + 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<iteratorType>() ) { + 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<CNumber>(); + //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<class N, class A> +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<class N> +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<class N> +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<class N, class RCT> +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<class N> +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<class N, class A> +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<FNumber>()); + - 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<Index>() ); + +/*--------------------------------------------------------------------------*/ + + void MCFGetRC( CRow CR , cIndex_Set nms = NULL , + cIndex strt = 0 , Index stp = Inf<Index>() ) ; + + CNumber MCFGetRC( cIndex i ); + +/*--------------------------------------------------------------------------*/ + + void MCFGetPi( CRow P , cIndex_Set nms = NULL , + cIndex strt = 0 , Index stp = Inf<Index>() ); + +/*--------------------------------------------------------------------------*/ + + 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<Index>() ); + + inline Index MCFSNde( cIndex i ); + + inline Index MCFENde( cIndex i ); + +/*--------------------------------------------------------------------------*/ + + void MCFCosts( CRow Costv , cIndex_Set nms = NULL , + cIndex strt = 0 , Index stp = Inf<Index>() ); + + inline CNumber MCFCost( cIndex i ); + +/*--------------------------------------------------------------------------*/ + + void MCFQCoef( CRow Qv , cIndex_Set nms = NULL , + cIndex strt = 0 , Index stp = Inf<Index>() ); + + inline CNumber MCFQCoef( cIndex i ); + +/*--------------------------------------------------------------------------*/ + + void MCFUCaps( FRow UCapv , cIndex_Set nms = NULL , + cIndex strt = 0 , Index stp = Inf<Index>() ); + + inline FNumber MCFUCap( cIndex i ); + +/*--------------------------------------------------------------------------*/ + + void MCFDfcts( FRow Dfctv , cIndex_Set nms = NULL , + cIndex strt = 0 , Index stp = Inf<Index>() ); + + 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<Index>() ); + + void ChgCost( Index arc , cCNumber NCost ); + +/*--------------------------------------------------------------------------*/ + + void ChgQCoef( cCRow NQCoef = NULL , cIndex_Set nms = NULL , + cIndex strt = 0 , Index stp = Inf<Index>() ); + + void ChgQCoef( Index arc , cCNumber NQCoef ); + +/*--------------------------------------------------------------------------*/ + + void ChgDfcts( cFRow NDfct , cIndex_Set nms = NULL , + cIndex strt = 0 , Index stp = Inf<Index>() ); + + void ChgDfct( Index nod , cFNumber NDfct ); + +/*--------------------------------------------------------------------------*/ + + void ChgUCaps( cFRow NCap , cIndex_Set nms = NULL , + cIndex strt = 0 , Index stp = Inf<Index>() ); + + 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<Index>() , + cIndex nEN = Inf<Index>() ); + + 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<class N, class A> + 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<class N> + 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<class N> + 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<class N, class RCT> + 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<class N> + 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<class N, class A> + inline N* Father( N *n, A *a ); + +/**< Method to find the predecessor of the node in the tree. */ + +/*--------------------------------------------------------------------------*/ + + #if(QUADRATICCOST) + template<class A> + inline FONumber ReductCost( A *a ); + #else + template<class A> + 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 <class A> +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 <class A> +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 Binary files differnew file mode 100644 index 0000000..f9378b6 --- /dev/null +++ b/impl/MCFSimplex.o 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 <stdlib.h> + + /* For random routines, see OPTrand() below. */ +#endif + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ + +#if( OPT_TIMERS <= 4 ) + #if( ( OPT_TIMERS == 1 ) || ( OPT_TIMERS == 3 ) ) + #include <sys/times.h> + #else + #include <sys/timeb.h> + #endif + #include <limits.h> +#elif( OPT_TIMERS == 5 ) + #include <time.h> +#elif( OPT_TIMERS == 6 ) + #include <sys/time.h> +#endif + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ + +#include <iostream> +#include <sstream> + +/* 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<class T> +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<Domain> { } }; +#include "MCFSimplex.h" + +template<typename Domain> +struct MinCostFlow : public Operator<Domain> { + MinCostFlow(const std::vector<Domain>& supplies, const std::vector<std::pair<int,int> > 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<MCFClass::FNumber>(); + } + 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<Domain>& 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<MCFClass::CNumber>()); + } + _solver.SolveMCF(); + if (_solver.MCFGetStatus() == MCFClass::kUnfeasible){ + return -infinity<Domain>(); + } else if (_solver.MCFGetFO() == MCFClass::Inf<MCFClass::FONumber>()) { + return infinity<Domain>(); + } else if (_solver.MCFGetFO() == -MCFClass::Inf<MCFClass::FONumber>()) { + return -infinity<Domain>(); + } 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<Domain> _supplies; + std::vector<std::pair<int,int> > _arcs; + mutable MCFSimplex _solver; +}; + /*#include "TemplateConstraintMatrix.hpp" template<typename Domain> diff --git a/impl/main.cpp b/impl/main.cpp index be9cc82..0daa72a 100644 --- a/impl/main.cpp +++ b/impl/main.cpp @@ -20,6 +20,59 @@ extern "C" { using namespace std; template<typename T> +vector<T> toSupplies(pANTLR3_BASE_TREE node) { + ANTLR3_UINT32 num = node->getChildCount(node); + T output; + vector<T> 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<int,int> toArc(pANTLR3_BASE_TREE node) { + pair<int,int> 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<pair<int,int> > toArcs(pANTLR3_BASE_TREE node) { + vector<pair<int,int> > 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<typename T> Expression<T>& treeToExpression(pANTLR3_BASE_TREE node, EquationSystem<T>& system) { ANTLR3_UINT32 num = node->getChildCount(node); string name = (char*) node->getText(node)->chars; @@ -39,7 +92,23 @@ Expression<T>& treeToExpression(pANTLR3_BASE_TREE node, EquationSystem<T>& syste } } - // anything that's not a constant/variable + if (name == "MCF") { + pANTLR3_BASE_TREE childNode; + + childNode = (pANTLR3_BASE_TREE) node->getChild(node, 0); + vector<T> supplies = toSupplies<T>(childNode); + childNode = (pANTLR3_BASE_TREE) node->getChild(node, 1); + vector<pair<int,int> > arcs = toArcs(childNode); + + vector<Expression<T>*> 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<T>(supplies, arcs), args); + } + + // anything that's not a constant/variable, or an MCF expr vector<Expression<T>*> args; pANTLR3_BASE_TREE childNode; for (unsigned int i = 0; i < num; ++i) { @@ -70,7 +139,7 @@ Expression<T>& treeToExpression(pANTLR3_BASE_TREE node, EquationSystem<T>& syste } else if (name == "GUARD" || name == "guard") { op = new Guard<T>(); } else { - std::cout << "throw exception" << *(char*)NULL; + assert(false); //throw "Parse error: Unknown operator"; } return system.expression(op, args); |