Skip to content

Commit d4b89bb

Browse files
committed
Merge pull request #47 from clue/move-algo-flow
Move flow and balance algorithms to new Algorithm\Flow
2 parents 88c09b7 + 777e0dc commit d4b89bb

File tree

5 files changed

+216
-90
lines changed

5 files changed

+216
-90
lines changed

src/Balance.php

Lines changed: 0 additions & 59 deletions
This file was deleted.

src/Flow.php

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
<?php
2+
3+
namespace Fhaculty\Graph\Algorithm;
4+
5+
use Fhaculty\Graph\Algorithm\BaseGraph;
6+
use Fhaculty\Graph\Edge\Base as Edge;
7+
use Fhaculty\Graph\Edge\Directed as EdgeDirected;
8+
use Fhaculty\Graph\Exception\UnexpectedValueException;
9+
use Fhaculty\Graph\Graph;
10+
use Fhaculty\Graph\Vertex;
11+
12+
/**
13+
* Basic algorithms for working with flow graphs
14+
*
15+
* A flow network (also known as a transportation network) is a directed graph
16+
* where each edge has a capacity and each edge receives a flow.
17+
*
18+
* @link http://en.wikipedia.org/wiki/Flow_network
19+
* @see Algorithm\Balance
20+
*/
21+
class Flow extends BaseSet
22+
{
23+
/**
24+
* check if this graph has any flow set (any edge has a non-NULL flow)
25+
*
26+
* @return boolean
27+
* @uses Edge::getFlow()
28+
*/
29+
public function hasFlow()
30+
{
31+
foreach ($this->set->getEdges() as $edge) {
32+
if ($edge->getFlow() !== NULL) {
33+
return true;
34+
}
35+
}
36+
37+
return false;
38+
}
39+
40+
/**
41+
* Calculates the flow for this Vertex: sum(outflow) - sum(inflow)
42+
*
43+
* Usually, vertices should have a resulting flow of 0: The sum of flows
44+
* entering a vertex must equal the sum of flows leaving a vertex. If the
45+
* resulting flow is < 0, this vertex is considered a sink (i.e. there's
46+
* more flow into this vertex). If the resulting flow is > 0, this vertex
47+
* is considered a "source" (i.e. there's more flow leaving this vertex).
48+
*
49+
* @param Vertex $vertex
50+
* @return float
51+
* @throws UnexpectedValueException if they are undirected edges
52+
* @see Vertex::getBalance()
53+
* @uses Vertex::getEdges()
54+
* @uses Edge::getFlow()
55+
*/
56+
public function getFlowVertex(Vertex $vertex)
57+
{
58+
$sumOfFlow = 0;
59+
60+
foreach ($vertex->getEdges() as $edge) {
61+
if (!($edge instanceof EdgeDirected)) {
62+
throw new UnexpectedValueException("TODO: undirected edges not suported yet");
63+
}
64+
65+
// edge is an outgoing edge of this vertex
66+
if ($edge->hasVertexStart($vertex)) {
67+
// flowing out (flow is "pointing away")
68+
$sumOfFlow += $edge->getFlow();
69+
// this is an ingoing edge
70+
} else {
71+
// flowing in
72+
$sumOfFlow -= $edge->getFlow();
73+
}
74+
}
75+
76+
return $sumOfFlow;
77+
}
78+
79+
public function getBalance()
80+
{
81+
$balance = 0;
82+
// Sum for all vertices of value
83+
foreach ($this->set->getVertices() as $vertex) {
84+
$balance += $vertex->getBalance();
85+
}
86+
87+
return $balance;
88+
}
89+
90+
/**
91+
* check if the current flow is balanced (aka "balanced flow" or "b-flow")
92+
*
93+
* a flow is considered balanced if each edge's current flow does not exceed its
94+
* maximum capacity (which is always guaranteed due to the implementation
95+
* of Edge::setFlow()) and each vertices' flow (i.e. outflow-inflow) equals
96+
* its balance.
97+
*
98+
* checking whether the FLOW is balanced is not to be confused with checking
99+
* whether the GRAPH is balanced (see Graph::isBalanced() instead)
100+
*
101+
* @return boolean
102+
* @see Algorithm\Degree::isBalanced() if you merely want to check indegree=outdegree
103+
* @uses self::getFlowVertex()
104+
* @uses Vertex::getBalance()
105+
*/
106+
public function isBalancedFlow()
107+
{
108+
// no need to check for each edge: flow <= capacity (setters already check that)
109+
// check for each vertex: outflow-inflow = balance
110+
foreach ($this->set->getVertices() as $vertex) {
111+
if ($this->getFlowVertex($vertex) !== $vertex->getBalance()) {
112+
return false;
113+
}
114+
}
115+
116+
return true;
117+
}
118+
}

src/MinimumCostFlow/SuccessiveShortestPath.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
class SuccessiveShortestPath extends Base
2020
{
2121
/**
22-
* @uses Vertex::getFlow()
2322
* @uses Graph::createGraphClone()
2423
* @uses AlgorithmResidualGraph::createGraph()
2524
* @uses AlgorithmSpMooreBellmanFord::getEdgesTo(Vertex $targetVertex)

tests/BalanceTest.php

Lines changed: 0 additions & 30 deletions
This file was deleted.

tests/FlowTest.php

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
<?php
2+
3+
use Fhaculty\Graph\Algorithm\Flow as AlgorithmFlow;
4+
use Fhaculty\Graph\Graph;
5+
6+
class FlowaTest extends TestCase
7+
{
8+
public function testGraphEmpty()
9+
{
10+
$graph = new Graph();
11+
12+
$alg = new AlgorithmFlow($graph);
13+
14+
$this->assertFalse($alg->hasFlow());
15+
$this->assertEquals(0, $alg->getBalance());
16+
$this->assertTrue($alg->isBalancedFlow());
17+
18+
return $graph;
19+
}
20+
21+
public function testEdgeWithZeroFlowIsConsideredFlow()
22+
{
23+
// 1 -> 2
24+
$graph = new Graph();
25+
$graph->createVertex(1)->createEdgeTo($graph->createVertex(2))->setFlow(0);
26+
27+
28+
$alg = new AlgorithmFlow($graph);
29+
30+
$this->assertTrue($alg->hasFlow());
31+
$this->assertEquals(0, $alg->getFlowVertex($graph->getVertex(1)));
32+
$this->assertEquals(0, $alg->getFlowVertex($graph->getVertex(2)));
33+
}
34+
35+
/**
36+
*
37+
* @param Graph $graph
38+
* @depends testGraphEmpty
39+
*/
40+
public function testGraphSimple(Graph $graph)
41+
{
42+
// 1 -> 2
43+
$graph->createVertex(1)->createEdgeTo($graph->createVertex(2));
44+
45+
$alg = new AlgorithmFlow($graph);
46+
47+
$this->assertFalse($alg->hasFlow());
48+
$this->assertEquals(0, $alg->getFlowVertex($graph->getVertex(1)));
49+
$this->assertEquals(0, $alg->getFlowVertex($graph->getVertex(2)));
50+
51+
return $graph;
52+
}
53+
54+
/**
55+
*
56+
* @param Graph $graph
57+
* @depends testGraphSimple
58+
*/
59+
public function testGraphWithUnweightedEdges(Graph $graph)
60+
{
61+
// additional flow edge: 2 -> 3
62+
$graph->getVertex(2)->createEdgeTo($graph->createVertex(3))->setFlow(10);
63+
64+
$alg = new AlgorithmFlow($graph);
65+
66+
$this->assertTrue($alg->hasFlow());
67+
$this->assertEquals(10, $alg->getFlowVertex($graph->getVertex(2)));
68+
$this->assertEquals(-10, $alg->getFlowVertex($graph->getVertex(3)));
69+
}
70+
71+
public function testGraphBalance()
72+
{
73+
// source(+100) -> sink(-10)
74+
$graph = new Graph();
75+
$graph->createVertex('source')->setBalance(100);
76+
$graph->createVertex('sink')->setBalance(-10);
77+
78+
$alg = new AlgorithmFlow($graph);
79+
80+
$this->assertEquals(90, $alg->getBalance());
81+
$this->assertFalse($alg->isBalancedFlow());
82+
}
83+
84+
/**
85+
* @expectedException UnexpectedValueException
86+
*/
87+
public function testVertexWithUndirectedEdgeHasInvalidFlow()
88+
{
89+
// 1 -- 2
90+
$graph = new Graph();
91+
$graph->createVertex(1)->createEdge($graph->createVertex(2))->setFlow(10);
92+
93+
94+
$alg = new AlgorithmFlow($graph);
95+
96+
$alg->getFlowVertex($graph->getVertex(1));
97+
}
98+
}

0 commit comments

Comments
 (0)