4
4
5
5
use Fhaculty \Graph \Algorithm \BaseVertex ;
6
6
use Fhaculty \Graph \Walk ;
7
- use Fhaculty \Graph \Exception \UnderflowException ;
7
+ use Fhaculty \Graph \Exception \OutOfBoundsException ;
8
8
use Fhaculty \Graph \Exception \InvalidArgumentException ;
9
9
use Fhaculty \Graph \Vertex ;
10
10
use Fhaculty \Graph \Edge \Base as Edge ;
11
11
use Fhaculty \Graph \Set \Edges ;
12
12
use Fhaculty \Graph \Set \Vertices ;
13
13
14
+ /**
15
+ * Abstract base class for shortest path algorithms
16
+ *
17
+ * This abstract base class provides the base interface for working with
18
+ * single-source shortest paths (SSSP).
19
+ *
20
+ * The shortest path problem is the problem of finding a path between two
21
+ * vertices such that the sum of the weights of its constituent edges is
22
+ * minimized. The weight of the shortest path is referred to as distance.
23
+ *
24
+ * A--[10]-------------B---E<--F
25
+ * \ /
26
+ * \--[4]--C--[2]--D
27
+ *
28
+ * In the above pictured graph, the distance (weight of the shortest path)
29
+ * between A and C is 4, and the shortest path between A and B is "A->C->D->B"
30
+ * with a distance (total weight) of 6.
31
+ *
32
+ * In graph theory, it is usually assumed that a path to an unreachable vertex
33
+ * has infinite distance. In the above pictured graph, there's no way path
34
+ * from A to F, i.e. vertex F is unreachable from vertex A because of the
35
+ * directed edge "E <- F" pointing in the opposite direction. This library
36
+ * considers this an Exception instead. So if you're asking for the distance
37
+ * between A and F, you'll receive an OutOfBoundsException instead.
38
+ *
39
+ * In graph theory, it is usually assumed that each vertex has a (pseudo-)path
40
+ * to itself with a distance of 0. In order to produce reliable, consistent
41
+ * results, this library considers this (pseudo-)path to be non-existant, i.e.
42
+ * there's NO "magic" path between A and A. So if you're asking for the distance
43
+ * between A and A, you'll receive an OutOfBoundsException instead. This allows
44
+ * us to check hether there's a real path between A and A (cycle via other
45
+ * vertices) as well as working with loop edges.
46
+ *
47
+ * @link http://en.wikipedia.org/wiki/Shortest_path_problem
48
+ * @link http://en.wikipedia.org/wiki/Tree_%28data_structure%29
49
+ * @see ShortestPath\Dijkstra
50
+ * @see ShortestPath\MooreBellmanFord which also supports negative Edge weights
51
+ * @see ShortestPath\BreadthFirst with does not consider Edge weights, but only the number of hops
52
+ */
14
53
abstract class Base extends BaseVertex
15
54
{
16
55
/**
17
56
* get walk (path) from start vertex to given end vertex
18
57
*
19
58
* @param Vertex $endVertex
20
59
* @return Walk
21
- * @throws Exception when there's no walk from start to end vertex
60
+ * @throws OutOfBoundsException if there's no path to the given end vertex
22
61
* @uses self::getEdgesTo()
23
62
* @uses Walk::factoryFromEdges()
24
63
*/
@@ -31,10 +70,10 @@ public function getWalkTo(Vertex $endVertex)
31
70
* get array of edges (path) from start vertex to given end vertex
32
71
*
33
72
* @param Vertex $endVertex
34
- * @throws Exception
73
+ * @throws OutOfBoundsException if there's no path to the given end vertex
35
74
* @return Edges
36
- * @uses AlgorithmSp ::getEdges()
37
- * @uses AlgorithmSp ::getEdgesToInternal()
75
+ * @uses self ::getEdges()
76
+ * @uses self ::getEdgesToInternal()
38
77
*/
39
78
public function getEdgesTo (Vertex $ endVertex )
40
79
{
@@ -46,15 +85,15 @@ public function getEdgesTo(Vertex $endVertex)
46
85
*
47
86
* @param Vertex $endVertex
48
87
* @param array $edges array of all input edges to operate on
49
- * @throws Exception
88
+ * @throws OutOfBoundsException if there's no path to the given vertex
50
89
* @return Edges
51
- * @uses AlgorithmSp ::getEdges() if no edges were given
90
+ * @uses self ::getEdges() if no edges were given
52
91
*/
53
92
protected function getEdgesToInternal (Vertex $ endVertex , array $ edges )
54
93
{
55
94
$ currentVertex = $ endVertex ;
56
95
$ path = array ();
57
- while ( $ currentVertex !== $ this -> vertex ) {
96
+ do {
58
97
$ pre = NULL ;
59
98
// check all edges to search for edge that points TO current vertex
60
99
foreach ($ edges as $ edge ) {
@@ -68,9 +107,9 @@ protected function getEdgesToInternal(Vertex $endVertex, array $edges)
68
107
} // ignore: this edge does not point TO current vertex
69
108
}
70
109
if ($ pre === NULL ) {
71
- throw new UnderflowException ('No edge leading to vertex ' );
110
+ throw new OutOfBoundsException ('No edge leading to vertex ' );
72
111
}
73
- }
112
+ } while ( $ currentVertex !== $ this -> vertex );
74
113
75
114
return new Edges (array_reverse ($ path ));
76
115
}
@@ -96,7 +135,7 @@ private function sumEdges(Edges $edges)
96
135
* get set of all Vertices the given start vertex has a path to
97
136
*
98
137
* @return Vertices
99
- * @uses AlgorithmSp ::getDistanceMap()
138
+ * @uses self ::getDistanceMap()
100
139
*/
101
140
public function getVertices ()
102
141
{
@@ -111,13 +150,31 @@ public function getVertices()
111
150
return new Vertices ($ vertices );
112
151
}
113
152
153
+ /**
154
+ * checks whether there's a path from this start vertex to given end vertex
155
+ *
156
+ * @param Vertex $endVertex
157
+ * @return boolean
158
+ * @uses self::getEdgesTo()
159
+ */
160
+ public function hasVertex (Vertex $ vertex )
161
+ {
162
+ try {
163
+ $ this ->getEdgesTo ($ vertex );
164
+ }
165
+ catch (OutOfBoundsException $ e ) {
166
+ return false ;
167
+ }
168
+ return true ;
169
+ }
170
+
114
171
/**
115
172
* get map of vertex IDs to distance
116
173
*
117
174
* @return float[]
118
- * @uses AlgorithmSp ::getEdges()
119
- * @uses AlgorithmSp ::getEdgesToInternal()
120
- * @uses AlgorithmSp ::sumEdges()
175
+ * @uses self ::getEdges()
176
+ * @uses self ::getEdgesToInternal()
177
+ * @uses self ::sumEdges()
121
178
*/
122
179
public function getDistanceMap ()
123
180
{
@@ -126,7 +183,7 @@ public function getDistanceMap()
126
183
foreach ($ this ->vertex ->getGraph ()->getVertices ()->getMap () as $ vid => $ vertex ) {
127
184
try {
128
185
$ ret [$ vid ] = $ this ->sumEdges ($ this ->getEdgesToInternal ($ vertex , $ edges ));
129
- } catch (UnderflowException $ ignore ) {
186
+ } catch (OutOfBoundsException $ ignore ) {
130
187
} // ignore vertices that can not be reached
131
188
}
132
189
@@ -138,9 +195,9 @@ public function getDistanceMap()
138
195
*
139
196
* @param Vertex $endVertex
140
197
* @return float
141
- * @throws Exception if given vertex is invalid or there's no path to given end vertex
142
- * @uses AlgorithmSp ::getEdgesTo()
143
- * @uses AlgorithmSp ::sumEdges()
198
+ * @throws OutOfBoundsException if there's no path to the given end vertex
199
+ * @uses self ::getEdgesTo()
200
+ * @uses self ::sumEdges()
144
201
*/
145
202
public function getDistance (Vertex $ endVertex )
146
203
{
@@ -150,8 +207,43 @@ public function getDistance(Vertex $endVertex)
150
207
/**
151
208
* create new resulting graph with only edges on shortest path
152
209
*
210
+ * The resulting Graph will always represent a tree with the start vertex
211
+ * being the root vertex.
212
+ *
213
+ * For example considering the following input Graph with equal weights on
214
+ * each edge:
215
+ *
216
+ * A----->F
217
+ * / \ ^
218
+ * / \ /
219
+ * / \ /
220
+ * | E
221
+ * | \
222
+ * | \
223
+ * B--->C<---D
224
+ *
225
+ * The resulting shortest path tree Graph will look like this:
226
+ *
227
+ * A----->F
228
+ * / \
229
+ * / \
230
+ * / \
231
+ * | E
232
+ * | \
233
+ * | \
234
+ * B--->C D
235
+ *
236
+ * Or by just arranging the Vertices slightly different:
237
+ *
238
+ * A
239
+ * /|\
240
+ * / | \
241
+ * B E \->F
242
+ * / |
243
+ * C<-/ D
244
+ *
153
245
* @return Graph
154
- * @uses AlgorithmSp ::getEdges()
246
+ * @uses self ::getEdges()
155
247
* @uses Graph::createGraphCloneEdges()
156
248
*/
157
249
public function createGraph ()
@@ -171,8 +263,6 @@ public function createGraph()
171
263
protected function getEdgesCheapestPredecesor (array $ predecessor )
172
264
{
173
265
$ vertices = $ this ->vertex ->getGraph ()->getVertices ()->getMap ();
174
- // start vertex doesn't have a predecessor
175
- unset($ vertices [$ this ->vertex ->getId ()]);
176
266
177
267
$ edges = array ();
178
268
foreach ($ vertices as $ vid => $ vertex ) {
0 commit comments