Skip to content

Commit 3ce5097

Browse files
committed
Merge pull request #74 from clue/fix-algo-minimumcostflow
Fix Algorithm\MinimumCostFlow
2 parents 23985a2 + 2b7b8b8 commit 3ce5097

7 files changed

Lines changed: 269 additions & 65 deletions

File tree

CHANGELOG.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,12 @@ you spot any mistakes.
3636
* Fix: Throwing an `UnexpectedValueException` if writing GraphViz Dot script
3737
to a temporary file fails and remove its debugging output
3838
([#77](https://github.com/clue/graph/issues/77) and [#78](https://github.com/clue/graph/issues/78) @Metabor)
39-
39+
40+
* Fix: The `Algorithm\MinimumCostFlow` algorithms now work again. The reference
41+
to a non-existant class has been updated. Also fixed several issues with
42+
regards to special cases such as disconnected or undirected graphs.
43+
([#74](https://github.com/clue/graph/issues/74)
44+
4045
* BC break: Remove unneeded alias definitions of `getVertexFirst()`,
4146
`getVertexSource()` and `getVertexTarget()`
4247
[#76]https://github.com/clue/graph/issues/76)):

lib/Fhaculty/Graph/Algorithm/MinimumCostFlow/Base.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use Fhaculty\Graph\Algorithm\BaseGraph;
66
use Fhaculty\Graph\Algorithm\Weight as AlgorithmWeight;
7+
use Fhaculty\Graph\Algorithm\Flow as AlgorithmFlow;
78
use Fhaculty\Graph\Exception\UnderflowException;
89
use Fhaculty\Graph\Edge\Base as Edge;
910
use Fhaculty\Graph\Set\Edges;
@@ -20,7 +21,9 @@ abstract class Base extends BaseGraph
2021
*/
2122
protected function checkBalance()
2223
{
23-
$balance = $this->graph->getBalance();
24+
$alg = new AlgorithmFlow($this->graph);
25+
$balance = $alg->getBalance();
26+
2427
$tolerance = 0.000001;
2528
if ($balance >= $tolerance || $balance <= -$tolerance) {
2629
throw new UnexpectedValueException('The given graph is not balanced value is: ' . $balance);

lib/Fhaculty/Graph/Algorithm/MinimumCostFlow/CycleCanceling.php

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,8 @@
22

33
namespace Fhaculty\Graph\Algorithm\MinimumCostFlow;
44

5-
65
use Fhaculty\Graph\Exception\UnexpectedValueException;
7-
86
use Fhaculty\Graph\Exception\UnderflowException;
9-
107
use Fhaculty\Graph\Edge\Base as Edge;
118
use Fhaculty\Graph\Set\Edges;
129
use Fhaculty\Graph\Algorithm\MaxFlow\EdmondsKarp as MaxFlowEdmondsKarp;
@@ -29,17 +26,16 @@ public function createGraph()
2926

3027
// connect supersource s* and supersink t* with all "normal" sources and sinks
3128
foreach ($resultGraph->getVertices() as $vertex) {
32-
// $vertex->getFlow();
33-
$flow = $vertex->getBalance();
34-
$b = abs($vertex->getBalance());
35-
// source
36-
if ($flow > 0) {
37-
$superSource->createEdgeTo($vertex)->setCapacity($b);
38-
39-
$sumBalance += $flow;
40-
// sink
41-
} elseif ($flow < 0) {
42-
$vertex->createEdgeTo($superSink)->setCapacity($b);
29+
$balance = $vertex->getBalance();
30+
31+
if ($balance > 0) {
32+
// positive balance => source capacity
33+
$superSource->createEdgeTo($vertex)->setCapacity($balance);
34+
35+
$sumBalance += $balance;
36+
} elseif ($balance < 0) {
37+
// negative balance => sink capacity (positive)
38+
$vertex->createEdgeTo($superSink)->setCapacity(-$balance);
4339
}
4440
}
4541

@@ -62,8 +58,8 @@ public function createGraph()
6258
$alg = new DetectNegativeCycle($residualGraph);
6359
try {
6460
$clonedEdges = $alg->getCycleNegative()->getEdges();
65-
// no negative cycle found => end algorithm
6661
} catch (UnderflowException $ignore) {
62+
// no negative cycle found => end algorithm
6763
break;
6864
}
6965

lib/Fhaculty/Graph/Algorithm/MinimumCostFlow/SuccessiveShortestPath.php

Lines changed: 21 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,8 @@
33
namespace Fhaculty\Graph\Algorithm\MinimumCostFlow;
44

55
use Fhaculty\Graph\Exception\DomainException;
6-
76
use Fhaculty\Graph\Exception\UnderflowException;
8-
97
use Fhaculty\Graph\Exception\UnexpectedValueException;
10-
118
use Fhaculty\Graph\Graph;
129
use Fhaculty\Graph\Vertex;
1310
use Fhaculty\Graph\Edge\Base as Edge;
@@ -21,10 +18,9 @@ class SuccessiveShortestPath extends Base
2118
{
2219
/**
2320
* @uses Graph::createGraphClone()
24-
* @uses AlgorithmResidualGraph::createGraph()
25-
* @uses AlgorithmSpMooreBellmanFord::getEdgesTo(Vertex $targetVertex)
26-
*
27-
* @see AlgorithmMCF::createGraph()
21+
* @uses ResidualGraph::createGraph()
22+
* @uses SpMooreBellmanFord::getEdgesTo(Vertex $targetVertex)
23+
* @see Base::createGraph()
2824
*/
2925
public function createGraph()
3026
{
@@ -40,29 +36,29 @@ public function createGraph()
4036
// initial flow of edges
4137
$edges = $resultGraph->getEdges();
4238
foreach ($edges as $edge) {
43-
// 0 if weight of edge is positiv
39+
if (!($edge instanceof EdgeDirected)) {
40+
throw new UnexpectedValueException('Undirected edges are not supported for SuccessiveShortestPath');
41+
}
42+
43+
// 0 if weight of edge is positive
4444
$flow = 0;
4545

4646
// maximal flow if weight of edge is negative
4747
if ($edge->getWeight() < 0) {
4848
$flow = $edge->getCapacity();
4949

50-
if ($edge instanceof EdgeDirected) {
51-
$startVertex = $edge->getVertexStart();
52-
$endVertex = $edge->getVertexEnd();
50+
$startVertex = $edge->getVertexStart();
51+
$endVertex = $edge->getVertexEnd();
5352

54-
// add balance to start- and end-vertex
55-
$this->addBalance($startVertex, $flow);
56-
$this->addBalance($endVertex, - $flow);
57-
} else {
58-
throw new UnexpectedValueException('Undirected Edges not suported');
59-
}
53+
// add balance to start- and end-vertex
54+
$this->addBalance($startVertex, $flow);
55+
$this->addBalance($endVertex, - $flow);
6056
}
6157

6258
$edge->setFlow($flow);
6359
}
6460

65-
// return or Exception insite this while
61+
// return or Exception inside this while
6662
while (true) {
6763
// create residual graph
6864
$algRG = new ResidualGraph($resultGraph);
@@ -71,25 +67,25 @@ public function createGraph()
7167
// search for a source
7268
try {
7369
$sourceVertex = $this->getVertexSource($residualGraph);
74-
// if no source is found the minimum-cost flow is found
7570
} catch (UnderflowException $ignore) {
71+
// no source is found => minimum-cost flow is found
7672
break;
7773
}
7874

79-
// search for reachble sink from this source
75+
// search for reachable target sink from this source
8076
try {
8177
$targetVertex = $this->getVertexSink($sourceVertex);
82-
// if no target is found the network has not enough capacity
83-
} catch (UnderflowException $ignore) {
84-
throw new UnexpectedValueException('The graph has not enough capacity for the minimum-cost flow');
78+
} catch (UnderflowException $e) {
79+
// no target found => network does not have enough capacity
80+
throw new UnexpectedValueException('The graph has not enough capacity for the minimum-cost flow', 0, $e);
8581
}
8682

8783
// calculate shortest path between source- and target-vertex
8884
$algSP = new SpMooreBellmanFord($sourceVertex);
8985
$edgesOnFlow = $algSP->getEdgesTo($targetVertex);
9086

9187
// calculate the maximal possible flow
92-
// new flow is the maximal possible flow for this path
88+
// new flow is the maximal possible flow for this path
9389
$newflow = $this->graph->getVertex($sourceVertex->getId())->getBalance() - $sourceVertex->getBalance();
9490
$targetFlow = - ($this->graph->getVertex($targetVertex->getId())->getBalance() - $targetVertex->getBalance());
9591

@@ -107,7 +103,7 @@ public function createGraph()
107103
// add the new flow to the path
108104
$this->addFlow($resultGraph, $edgesOnFlow, $newflow);
109105

110-
// add balance to source and remove for the sink
106+
// add balance to source and remove for the target sink
111107
$oriSourceVertex = $resultGraph->getVertex($sourceVertex->getId());
112108
$oriTargetVertex = $resultGraph->getVertex($targetVertex->getId());
113109

@@ -118,29 +114,6 @@ public function createGraph()
118114
return $resultGraph;
119115
}
120116

121-
/**
122-
* check if balance on each vertex of the given graph matches the original graph's
123-
*
124-
* @param Graph $graph
125-
* @return boolean
126-
* @throws Exception if given graph is not a clone of the original graph (each vertex has to be present in both graphs)
127-
* @uses Graph::getBalanace()
128-
* @uses Graph::getVertex()
129-
*/
130-
private function isBalanceReached(Graph $graph)
131-
{
132-
if (count($graph->getVertices()) !== count($this->graph->getVertices())) {
133-
throw new DomainException('Given graph does not appear to be a clone of input graph');
134-
}
135-
foreach ($this->graph->getVertices()->getMap() as $vid => $vertex) {
136-
if ($vertex->getBalance() !== $graph->getVertex($vid)->getBalance()) {
137-
return false;
138-
}
139-
}
140-
141-
return true;
142-
}
143-
144117
/**
145118
*
146119
*

0 commit comments

Comments
 (0)