Skip to content

Commit 1330ef0

Browse files
committed
Merge branch 'fix-mbf' into test-detectnegativecycle
2 parents 664e85a + b795dda commit 1330ef0

File tree

7 files changed

+190
-8
lines changed

7 files changed

+190
-8
lines changed

src/Directed.php

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use Fhaculty\Graph\Algorithm\BaseGraph;
66
use Fhaculty\Graph\Edge\Directed as EdgeDirected;
7+
use Fhaculty\Graph\Edge\Undirected as EdgeUndirected;
78

89
/**
910
* Basic algorithms for working with the undirected or directed Graphs (digraphs) / Walks.
@@ -14,11 +15,14 @@
1415
class Directed extends BaseDual
1516
{
1617
/**
17-
* checks whether the graph has any directed edges (aka digraph)
18+
* checks whether the graph has any directed edges
19+
*
20+
* This method is intentionally not named "isDirected()" (aka digraph),
21+
* because that might be misleading in regards to empty and/or mixed graphs.
1822
*
1923
* @return boolean
2024
*/
21-
public function isDirected()
25+
public function hasDirected()
2226
{
2327
foreach ($this->set->getEdges() as $edge) {
2428
if ($edge instanceof EdgeDirected) {
@@ -28,4 +32,35 @@ public function isDirected()
2832

2933
return false;
3034
}
35+
36+
/**
37+
* checks whether the graph has any undirected edges
38+
*
39+
* This method is intentionally not named "isUndirected()",
40+
* because that might be misleading in regards to empty and/or mixed graphs.
41+
*
42+
* @return boolean
43+
*/
44+
public function hasUndirected()
45+
{
46+
foreach ($this->set->getEdges() as $edge) {
47+
if ($edge instanceof EdgeUndirected) {
48+
return true;
49+
}
50+
}
51+
52+
return false;
53+
}
54+
55+
/**
56+
* checks whether this is a mixed graph (contains both directed and undirected edges)
57+
*
58+
* @return boolean
59+
* @uses self::hasDirected()
60+
* @uses self::hasUndirected()
61+
*/
62+
public function isMixed()
63+
{
64+
return ($this->hasDirected() && $this->hasUndirected());
65+
}
3166
}

src/MaximumMatching/Flow.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ class Flow extends Base
1818
public function getEdges()
1919
{
2020
$alg = new Directed($this->graph);
21-
if ($alg->isDirected()) {
21+
if ($alg->hasDirected()) {
2222
throw new UnexpectedValueException('Input graph contains directed edges');
2323
}
2424

src/ShortestPath/MooreBellmanFord.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ private function bigStep(Edges $edges, array &$totalCostOfCheapestPathTo, array
4343
// New possible costs of this path
4444
$newCost = $totalCostOfCheapestPathTo[$fromVertex->getId()] + $edge->getWeight();
4545
if (is_infinite($newCost)) {
46-
$newCost = $edge->getWeight();
46+
$newCost = $edge->getWeight() + 0;
4747
}
4848

4949
// No path has been found yet

tests/ConnectedComponentsTest.php

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
<?php
2+
3+
use Fhaculty\Graph\Algorithm\ConnectedComponents as AlgorithmConnected;
4+
use Fhaculty\Graph\Graph;
5+
6+
class ConnectedComponentsTest extends TestCase
7+
{
8+
public function testNullGraph()
9+
{
10+
$graph = new Graph();
11+
12+
$alg = new AlgorithmConnected($graph);
13+
14+
$this->assertEquals(0, $alg->getNumberOfComponents());
15+
$this->assertFalse($alg->isSingle());
16+
$this->assertCount(0, $alg->createGraphsComponents());
17+
}
18+
19+
public function testGraphSingleTrivial()
20+
{
21+
$graph = new Graph();
22+
$graph->createVertex(1);
23+
24+
$alg = new AlgorithmConnected($graph);
25+
26+
$this->assertEquals(1, $alg->getNumberOfComponents());
27+
$this->assertTrue($alg->isSingle());
28+
29+
$graphs = $alg->createGraphsComponents();
30+
31+
$this->assertCount(1, $graphs);
32+
$this->assertGraphEquals($graph, reset($graphs));
33+
}
34+
35+
public function testGraphEdgeDirections()
36+
{
37+
// 1 -- 2 -> 3 <- 4
38+
$graph = new Graph();
39+
$graph->createVertex(1)->createEdge($graph->createVertex(2));
40+
$graph->getVertex(2)->createEdgeTo($graph->createVertex(3));
41+
$graph->createVertex(4)->createEdgeTo($graph->getVertex(3));
42+
43+
$alg = new AlgorithmConnected($graph);
44+
45+
$this->assertEquals(1, $alg->getNumberOfComponents());
46+
$this->assertTrue($alg->isSingle());
47+
48+
$graphs = $alg->createGraphsComponents();
49+
50+
$this->assertCount(1, $graphs);
51+
$this->assertGraphEquals($graph, reset($graphs));
52+
$this->assertGraphEquals($graph, $alg->createGraphComponentVertex($graph->getVertex(1)));
53+
}
54+
55+
public function testComponents()
56+
{
57+
// 1 -- 2, 3 -> 4, 5
58+
$graph = new Graph();
59+
$v1 = $graph->createVertex(1);
60+
$v2 = $graph->createVertex(2);
61+
$v3 = $graph->createVertex(3);
62+
$v4 = $graph->createVertex(4);
63+
$v5 = $graph->createVertex(5);
64+
$e1 = $v1->createEdge($v2);
65+
$e2 = $v3->createEdgeTo($v4);
66+
67+
$alg = new AlgorithmConnected($graph);
68+
69+
$this->assertEquals(3, $alg->getNumberOfComponents());
70+
$this->assertFalse($alg->isSingle());
71+
72+
$graphs = $alg->createGraphsComponents();
73+
$this->assertCount(3, $graphs);
74+
75+
$ge = new Graph();
76+
$ge->createVertex(1)->createEdge($ge->createVertex(2));
77+
$this->assertGraphEquals($ge, $alg->createGraphComponentVertex($v2));
78+
79+
$ge = new Graph();
80+
$ge->createVertex(5);
81+
$this->assertEquals($ge, $alg->createGraphComponentVertex($v5));
82+
}
83+
84+
/**
85+
* @expectedException InvalidArgumentException
86+
*/
87+
public function testInvalidVertexPassedToAlgorithm()
88+
{
89+
$graph = new Graph();
90+
91+
$graph2 = new Graph();
92+
$v2 = $graph2->createVertex(12);
93+
94+
$alg = new AlgorithmConnected($graph);
95+
$alg->createGraphComponentVertex($v2);
96+
}
97+
}

tests/DirectedTest.php

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ public function testGraphEmpty()
1111

1212
$alg = new AlgorithmDirected($graph);
1313

14-
$this->assertFalse($alg->isDirected());
14+
$this->assertFalse($alg->hasDirected());
15+
$this->assertFalse($alg->hasUndirected());
16+
$this->assertFalse($alg->isMixed());
1517
}
1618

1719
public function testGraphUndirected()
@@ -22,7 +24,9 @@ public function testGraphUndirected()
2224

2325
$alg = new AlgorithmDirected($graph);
2426

25-
$this->assertFalse($alg->isDirected());
27+
$this->assertFalse($alg->hasDirected());
28+
$this->assertTrue($alg->hasUndirected());
29+
$this->assertFalse($alg->isMixed());
2630
}
2731

2832
public function testGraphDirected()
@@ -33,7 +37,9 @@ public function testGraphDirected()
3337

3438
$alg = new AlgorithmDirected($graph);
3539

36-
$this->assertTrue($alg->isDirected());
40+
$this->assertTrue($alg->hasDirected());
41+
$this->assertFalse($alg->hasUndirected());
42+
$this->assertFalse($alg->isMixed());
3743
}
3844

3945
public function testGraphMixed()
@@ -45,6 +51,8 @@ public function testGraphMixed()
4551

4652
$alg = new AlgorithmDirected($graph);
4753

48-
$this->assertTrue($alg->isDirected());
54+
$this->assertTrue($alg->hasDirected());
55+
$this->assertTrue($alg->hasUndirected());
56+
$this->assertTrue($alg->isMixed());
4957
}
5058
}

tests/ShortestPath/BaseShortestPathTest.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,24 @@ public function testSeparateGraphsAreNotReachable()
116116
$alg->getEdgesTo($vg2);
117117
}
118118

119+
public function testGraphUnweighted()
120+
{
121+
// 1 -> 2
122+
$graph = new Graph();
123+
$v1 = $graph->createVertex(1);
124+
$v2 = $graph->createVertex(2);
125+
$e1 = $v1->createEdgeTo($v2);
126+
127+
$alg = $this->createAlg($v1);
128+
129+
$expectedWeight = $this->getExpectedWeight(array($e1));
130+
$this->assertEquals($expectedWeight, $alg->getDistance($v2));
131+
$this->assertEquals(array(2 => $expectedWeight), $alg->getDistanceMap());
132+
$this->assertEquals(array($e1), $alg->getEdges()->getVector());
133+
$this->assertEquals(array($e1), $alg->getEdgesTo($v2)->getVector());
134+
$this->assertEquals(array(2), $alg->getVertices()->getIds());
135+
}
136+
119137
public function testGraphTwoComponents()
120138
{
121139
// 1 -[10]-> 2

tests/ShortestPath/MooreBellmanFordTest.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,4 +69,28 @@ public function testLoopNegativeWeightIsCycle()
6969

7070
$cycle = $alg->getCycleNegative();
7171
}
72+
73+
public function testNegativeComponentHasCycle()
74+
{
75+
// 1 -[1]-> 2 3 --[-1]--> 4
76+
// ^ |
77+
// \---[-2]----/
78+
$graph = new Graph();
79+
$v1 = $graph->createVertex(1);
80+
$v2 = $graph->createVertex(2);
81+
$v3 = $graph->createVertex(3);
82+
$v4 = $graph->createVertex(4);
83+
$e1 = $v1->createEdgeTo($v2)->setWeight(1);
84+
$e2 = $v3->createEdgeTo($v4)->setWeight(-1);
85+
$e3 = $v4->createEdgeTo($v3)->setWeight(-2);
86+
87+
// second component has a cycle
88+
$alg = $this->createAlg($v3);
89+
$cycle = $alg->getCycleNegative();
90+
91+
// first component does not have a cycle
92+
$alg = $this->createAlg($v1);
93+
$this->setExpectedException('UnderflowException');
94+
$alg->getCycleNegative();
95+
}
7296
}

0 commit comments

Comments
 (0)