You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
449 lines
10 KiB
449 lines
10 KiB
/* |
|
######################################################### |
|
# # |
|
# IBFSGraph - Software for solving # |
|
# Maximum s-t Flow / Minimum s-t Cut # |
|
# using the IBFS algorithm # |
|
# # |
|
# http://www.cs.tau.ac.il/~sagihed/ibfs/ # |
|
# # |
|
# Haim Kaplan (haimk@cs.tau.ac.il) # |
|
# Sagi Hed (sagihed@post.tau.ac.il) # |
|
# # |
|
# 2015 - modified by cDc@seacave # |
|
# # |
|
######################################################### |
|
|
|
This software implements the IBFS (Incremental Breadth First Search) maximum flow algorithm from |
|
"Maximum flows by incremental breadth-first search" |
|
Andrew V. Goldberg, Sagi Hed, Haim Kaplan, Robert E. Tarjan, and Renato F. Werneck. |
|
In Proceedings of the 19th European conference on Algorithms, ESA'11, pages 457-468. |
|
ISBN 978-3-642-23718-8 |
|
2011 |
|
|
|
Copyright Haim Kaplan (haimk@cs.tau.ac.il) and Sagi Hed (sagihed@post.tau.ac.il) |
|
|
|
########### |
|
# LICENSE # |
|
########### |
|
This software can be used for research purposes only. |
|
If you use this software for research purposes, you should cite the aforementioned paper |
|
in any resulting publication and appropriately credit it. |
|
|
|
If you require another license, please contact the above. |
|
|
|
########### |
|
# USAGE # |
|
########### |
|
|
|
IBFSGraph g = new IBFSGraph(); |
|
|
|
// g.initSize(numNodes, numEdges) indicate the number of nodes and edges in the graph. |
|
// Number of edges does not include edges from the source and to the sink! |
|
g->initSize(4, 5); |
|
|
|
// g.addNode(nodeID, capFromSource, capToSink) indicate node is connected |
|
// to source and sink with appropriate capacities |
|
// nodeID is between 0 ... numNodes as supplied in initSize. |
|
g->addNode(0, 500, 100); |
|
g->addNode(1, 200, 0); |
|
g->addNode(2, 50, 50); |
|
g->addNode(3, 0, 0); // can discard this line |
|
|
|
// g.addEdge(fromNodeID, toNodeID, capForward, capReverse) indicate edge |
|
// connect fromNodeID and toNodeID |
|
// with appropriate forward capacity and reverse capacity. |
|
g->addEdge(0, 1, 100, 40); |
|
g->addEdge(0, 2, 200, 800); |
|
g->addEdge(0, 3, 500, 500); |
|
g->addEdge(1, 3, 300, 100); |
|
g->addEdge(2, 3, 500, 500); |
|
|
|
long startTime = getTime(); |
|
g->initGraph(); |
|
g->computeMaxFlow(); |
|
long time = getTime()-startTime; |
|
|
|
fprintf(stdout, "time=%d\n", time); |
|
fprintf(stdout, "flow=%d\n", g->getFlow()); |
|
for (int i=0; i < 3; i++) |
|
fprintf("node %d has label %d\n", i, g->isNodeOnSrcSide(i)); |
|
|
|
*/ |
|
|
|
|
|
#ifndef _IBFS_H__ |
|
#define _IBFS_H__ |
|
|
|
#include <stdio.h> |
|
#include <string.h> |
|
#include <assert.h> |
|
|
|
|
|
#ifndef IBIO |
|
#define IBIO 0 |
|
#endif |
|
#ifndef IBTEST |
|
#define IBTEST 0 |
|
#endif |
|
#ifndef IBSTATS |
|
#define IBSTATS 0 |
|
#endif |
|
#ifndef IBDEBUG |
|
#define IBDEBUG(X) fprintf(stdout, X"\n"); fflush(stdout) |
|
#endif |
|
|
|
#define IB_ALTERNATE_SMART 1 |
|
#define IB_ORPHANS_END ((Node*)1) |
|
|
|
|
|
namespace IBFS { |
|
|
|
typedef float Real; |
|
typedef float EdgeCap; |
|
|
|
class IBFSStats |
|
{ |
|
public: |
|
IBFSStats() |
|
{ |
|
Real C = (IBSTATS ? 0 : -1); |
|
augs=C; |
|
growthS=C; |
|
growthT=C; |
|
orphans=C; |
|
growthArcs=C; |
|
pushes=C; |
|
orphanArcs1=C; |
|
orphanArcs2=C; |
|
orphanArcs3=C; |
|
if (IBSTATS) augLenMin = (1 << 30); |
|
else augLenMin=C; |
|
augLenMax=C; |
|
} |
|
void inline incAugs() {if (IBSTATS) augs++;} |
|
Real inline getAugs() {return augs;} |
|
void inline incGrowthS() {if (IBSTATS) growthS++;} |
|
Real inline getGrowthS() {return growthS;} |
|
void inline incGrowthT() {if (IBSTATS) growthT++;} |
|
Real inline getGrowthT() {return growthT;} |
|
void inline incOrphans() {if (IBSTATS) orphans++;} |
|
Real inline getOrphans() {return orphans;} |
|
void inline incGrowthArcs() {if (IBSTATS) growthArcs++;} |
|
Real inline getGrowthArcs() {return growthArcs;} |
|
void inline incPushes() {if (IBSTATS) pushes++;} |
|
Real inline getPushes() {return pushes;} |
|
void inline incOrphanArcs1() {if (IBSTATS) orphanArcs1++;} |
|
Real inline getOrphanArcs1() {return orphanArcs1;} |
|
void inline incOrphanArcs2() {if (IBSTATS) orphanArcs2++;} |
|
Real inline getOrphanArcs2() {return orphanArcs2;} |
|
void inline incOrphanArcs3() {if (IBSTATS) orphanArcs3++;} |
|
Real inline getOrphanArcs3() {return orphanArcs3;} |
|
void inline addAugLen(Real len) { |
|
if (IBSTATS) { |
|
if (len > augLenMax) augLenMax = len; |
|
if (len < augLenMin) augLenMin = len; |
|
} |
|
} |
|
Real inline getAugLenMin() {return augLenMin;} |
|
Real inline getAugLenMax() {return augLenMax;} |
|
|
|
private: |
|
Real augs; |
|
Real growthS; |
|
Real growthT; |
|
Real orphans; |
|
Real growthArcs; |
|
Real pushes; |
|
Real orphanArcs1; |
|
Real orphanArcs2; |
|
Real orphanArcs3; |
|
Real augLenMin; |
|
Real augLenMax; |
|
}; |
|
|
|
|
|
|
|
class IBFSGraph |
|
{ |
|
public: |
|
IBFSGraph(); |
|
~IBFSGraph(); |
|
void setVerbose(bool a_verbose) { |
|
verbose = a_verbose; |
|
} |
|
|
|
void initSize(int numNodes, int numEdges); |
|
void addEdge(int nodeIndexFrom, int nodeIndexTo, EdgeCap capacity, EdgeCap reverseCapacity); |
|
void addNode(int nodeIndex, EdgeCap capacityFromSource, EdgeCap capacityToSink); |
|
|
|
void setCompactSlowInitMode(bool a_compactSlowInitMode) { |
|
compactSlowInitMode = a_compactSlowInitMode; |
|
} |
|
void initGraph(); |
|
EdgeCap computeMaxFlow(); |
|
|
|
inline IBFSStats getStats() { |
|
return stats; |
|
} |
|
inline EdgeCap getFlow() { |
|
return flow; |
|
} |
|
inline size_t getNumNodes() { |
|
return nodeEnd-nodes; |
|
} |
|
inline size_t getNumArcs() { |
|
return arcEnd-arcs; |
|
} |
|
bool isNodeOnSrcSide(int nodeIndex) const; |
|
|
|
private: |
|
struct Node; |
|
struct Arc; |
|
|
|
struct Arc |
|
{ |
|
Node* head; |
|
Arc* rev; |
|
#if 0 |
|
int isRevResidual :1; |
|
int rCap :31; |
|
#else |
|
EdgeCap rCap; |
|
unsigned char isRevResidual; |
|
#endif |
|
}; |
|
|
|
struct Node |
|
{ |
|
int lastAugTimestamp:31; |
|
int isParentCurr:1; |
|
Arc *firstArc; |
|
Arc *parent; |
|
Node *firstSon; |
|
Node *nextPtr; |
|
int label; // label > 0: distance from s, label < 0: -distance from t |
|
EdgeCap excess; // excess > 0: capacity from s, excess < 0: -capacity to t |
|
}; |
|
|
|
class ActiveList |
|
{ |
|
public: |
|
inline ActiveList() { |
|
list = NULL; |
|
len = 0; |
|
} |
|
inline void init(int numNodes) { |
|
list = new Node*[numNodes]; |
|
len = 0; |
|
} |
|
inline void release() { |
|
if (list != NULL) { |
|
delete[] list; |
|
list = NULL; |
|
} |
|
} |
|
inline void clear() { |
|
len = 0; |
|
} |
|
inline void add(Node* x) { |
|
list[len] = x; |
|
len++; |
|
} |
|
inline static void swapLists(ActiveList *a, ActiveList *b) { |
|
ActiveList tmp = (*a); |
|
(*a) = (*b); |
|
(*b) = tmp; |
|
} |
|
Node **list; |
|
int len; |
|
}; |
|
|
|
class Buckets |
|
{ |
|
public: |
|
inline Buckets() { |
|
buckets = NULL; |
|
prevPtrs = NULL; |
|
maxBucket = 0; |
|
nodes = NULL; |
|
} |
|
inline void init(Node *a_nodes, int numNodes) { |
|
nodes = a_nodes; |
|
buckets = new Node*[numNodes]; |
|
memset(buckets, 0, sizeof(Node*)*numNodes); |
|
prevPtrs = new Node*[numNodes]; |
|
memset(prevPtrs, 0, sizeof(Node*)*numNodes); |
|
maxBucket = 0; |
|
} |
|
inline void release() { |
|
if (buckets != NULL) { |
|
delete[] buckets; |
|
buckets = NULL; |
|
} |
|
if (prevPtrs != NULL) { |
|
delete[] prevPtrs; |
|
prevPtrs = NULL; |
|
} |
|
} |
|
template <bool sTree> inline void add(Node* x) { |
|
int bucket = (sTree ? (x->label) : (-x->label)); |
|
if (buckets[bucket] == NULL || buckets[bucket] == IB_ORPHANS_END) { |
|
x->nextPtr = IB_ORPHANS_END; |
|
} else { |
|
x->nextPtr = buckets[bucket]; |
|
prevPtrs[x->nextPtr-nodes] = x; |
|
} |
|
buckets[bucket] = x; |
|
if (bucket > maxBucket) maxBucket = bucket; |
|
} |
|
inline Node* popFront(int bucket) { |
|
Node *x = buckets[bucket]; |
|
if (x == NULL || x == IB_ORPHANS_END) return NULL; |
|
buckets[bucket] = x->nextPtr; |
|
//x->nextOrphan = NULL; |
|
return x; |
|
} |
|
template <bool sTree> inline void remove(Node *x) { |
|
int bucket = (sTree ? (x->label) : (-x->label)); |
|
if (buckets[bucket] == x) { |
|
buckets[bucket] = x->nextPtr; |
|
} else { |
|
prevPtrs[x-nodes]->nextPtr = x->nextPtr; |
|
if (x->nextPtr != IB_ORPHANS_END) prevPtrs[x->nextPtr-nodes] = prevPtrs[x-nodes]; |
|
} |
|
//x->nextOrphan = NULL; |
|
} |
|
|
|
Node **buckets; |
|
Node **prevPtrs; |
|
Node *nodes; |
|
int maxBucket; |
|
}; |
|
|
|
// members |
|
IBFSStats stats; |
|
Node *nodes, *nodeEnd; |
|
Arc *arcs, *arcEnd; |
|
int numNodes; |
|
EdgeCap flow; |
|
unsigned short augTimestamp; |
|
unsigned int uniqOrphansS, uniqOrphansT; |
|
Node* orphanFirst; |
|
Node* orphanLast; |
|
int topLevelS, topLevelT; |
|
ActiveList active0, activeS1, activeT1; |
|
Buckets orphanBuckets; |
|
bool verbose; |
|
|
|
void augment(Arc *bridge); |
|
template <bool sTree> void augmentTree(Node *x, EdgeCap bottleneck); |
|
template <bool sTree> void adoption(); |
|
template <bool sTree> void adoption3Pass(); |
|
template <bool dirS> void growth(); |
|
|
|
#if IBIO>0 |
|
bool readFromFile(char *filename); |
|
bool readFromFileCompile(char *filename); |
|
bool readFromFile(char *filename, bool checkCompile); |
|
bool readCompiled(FILE *pFile); |
|
#endif |
|
|
|
// |
|
// Initialization |
|
// |
|
struct TmpEdge |
|
{ |
|
Node* head; |
|
Node* tail; |
|
EdgeCap cap; |
|
EdgeCap revCap; |
|
}; |
|
struct TmpArc |
|
{ |
|
TmpArc *rev; |
|
EdgeCap cap; |
|
}; |
|
char *memArcs; |
|
TmpEdge *tmpEdges, *tmpEdgeLast; |
|
TmpArc *tmpArcs; |
|
bool compactSlowInitMode; |
|
void initGraphFast(); |
|
void initGraphCompact(); |
|
|
|
// |
|
// Testing |
|
// |
|
void testTree(); |
|
void testExit() { |
|
exit(1); |
|
} |
|
inline void testNode(Node *x) { |
|
if (IBTEST && x-nodes == -1) { |
|
IBDEBUG("*"); |
|
} |
|
} |
|
}; |
|
|
|
|
|
inline void IBFSGraph::addNode(int nodeIndex, EdgeCap capacitySource, EdgeCap capacitySink) |
|
{ |
|
EdgeCap f = nodes[nodeIndex].excess; |
|
if (f > 0) { |
|
capacitySource += f; |
|
} else { |
|
capacitySink -= f; |
|
} |
|
if (capacitySource < capacitySink) { |
|
flow += capacitySource; |
|
} else { |
|
flow += capacitySink; |
|
} |
|
nodes[nodeIndex].excess = capacitySource - capacitySink; |
|
} |
|
|
|
inline void IBFSGraph::addEdge(int nodeIndexFrom, int nodeIndexTo, EdgeCap capacity, EdgeCap reverseCapacity) |
|
{ |
|
assert((void*)tmpEdgeLast < (void*)tmpArcs); |
|
tmpEdgeLast->tail = nodes + nodeIndexFrom; |
|
tmpEdgeLast->head = nodes + nodeIndexTo; |
|
tmpEdgeLast->cap = capacity; |
|
tmpEdgeLast->revCap = reverseCapacity; |
|
tmpEdgeLast++; |
|
|
|
// use label as a temporary storage |
|
// to count the out degree of nodes |
|
nodes[nodeIndexFrom].label++; |
|
nodes[nodeIndexTo].label++; |
|
|
|
/* |
|
Arc *aFwd = arcLast; |
|
arcLast++; |
|
Arc *aRev = arcLast; |
|
arcLast++; |
|
|
|
Node* x = nodes + nodeIndexFrom; |
|
x->label++; |
|
Node* y = nodes + nodeIndexTo; |
|
y->label++; |
|
|
|
aRev->rev = aFwd; |
|
aFwd->rev = aRev; |
|
aFwd->rCap = capacity; |
|
aRev->rCap = reverseCapacity; |
|
aFwd->head = y; |
|
aRev->head = x;*/ |
|
} |
|
|
|
|
|
inline bool IBFSGraph::isNodeOnSrcSide(int nodeIndex) const |
|
{ |
|
if (nodes[nodeIndex].label == numNodes || nodes[nodeIndex].label == 0) { |
|
return activeT1.len == 0; |
|
} |
|
return (nodes[nodeIndex].label > 0); |
|
} |
|
|
|
} // namespace IBFS |
|
|
|
#endif
|
|
|