src/Algo/FloydWarshall.php
<?php
/**
* Floyd-Warshall algorithm for finding the shortest path with path reconstruction.
*/
namespace GraphDS\Algo;
use GraphDS\Graph\Graph;
use GraphDS\Graph\DirectedGraph;
use GraphDS\Graph\UndirectedGraph;
use InvalidArgumentException;
/**
* Class defining the Floyd-Warshall algorithm.
*/
class FloydWarshall
{
/**
* Reference to the graph.
*
* @var Graph
*/
public $graph;
/**
* Array holding the shortest distances to each vertex from the source.
*
* @var array
*/
public $dist = array();
/**
* Array holding the next vertices for each vertex for the shortest path.
*
* @var array
*/
public $next = array();
/**
* Constructor for the Floyd-Warshall algorithm.
*
* @param Graph $graph The graph to which the Floyd-Warshall should be applied
*
* @throws InvalidArgumentException
*/
public function __construct(Graph $graph)
{
if (!($graph instanceof DirectedGraph) && !($graph instanceof UndirectedGraph)) {
throw new InvalidArgumentException(
"Floyd-Warshall shortest path algorithm requires a directed or undirected graph"
);
}
$this->graph = &$graph;
}
/**
* Calculates the shortest paths in the graph using the Floyd-Warshall algorithm.
*/
public function run()
{
foreach (array_keys($this->graph->vertices) as $vertex1) {
foreach (array_keys($this->graph->vertices) as $vertex2) {
$this->dist[$vertex1][$vertex2] = INF;
$this->next[$vertex1][$vertex2] = null;
$this->dist[$vertex1][$vertex1] = 0;
}
}
foreach ($this->graph->edges as $vertex1 => $vertex1Value) {
foreach ($vertex1Value as $vertex2 => $vertex2Value) {
$this->dist[$vertex1][$vertex2] = $vertex2Value->getValue();
$this->next[$vertex1][$vertex2] = $vertex2;
if ($this->graph instanceof UndirectedGraph) {
$this->dist[$vertex2][$vertex1] = $this->dist[$vertex1][$vertex2];
$this->next[$vertex2][$vertex1] = $vertex1;
}
}
}
foreach (array_keys($this->graph->vertices) as $k) {
foreach (array_keys($this->graph->vertices) as $i) {
foreach (array_keys($this->graph->vertices) as $j) {
if ($this->dist[$i][$j] > ($this->dist[$i][$k] + $this->dist[$k][$j])) {
$this->dist[$i][$j] = $this->dist[$i][$k] + $this->dist[$k][$j];
$this->next[$i][$j] = $this->next[$i][$k];
}
}
}
}
}
/**
* Returns the shortest path from vertex $start to $dest in the graph.
*
* @param mixed $start ID of the start vertex
* @param mixed $dest ID of the destination vertex
*
* @return array|null An array containing the shortest path and distance
*/
public function get($start, $dest)
{
$startReal = $start;
$path = array($start);
while ($start !== $dest) {
if (!($start = $this->next[$start][$dest])) {
return null;
}
$path[] = $start;
}
return array(
'path' => $path,
'dist' => $this->dist[$startReal][$dest],
);
}
}