Skip to content

Commit

Permalink
fix shortest path
Browse files Browse the repository at this point in the history
  • Loading branch information
sporchia committed May 3, 2020
1 parent c7b4c5a commit 105a02d
Show file tree
Hide file tree
Showing 7 changed files with 152 additions and 112 deletions.
18 changes: 8 additions & 10 deletions src/ShortestPath/BreadthFirst.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
use OutOfBoundsException;
use PHGraph\Contracts\ShortestPath;
use PHGraph\Graph;
use PHGraph\Support\EdgeCollection;
use PHGraph\Vertex;
use PHGraph\Walk;

Expand Down Expand Up @@ -97,7 +96,7 @@ public function getEdgesMap(): array
$vertex_queue = [];
$vertex_current = $this->vertex;
$edges = [
$vertex_current->getId() => new EdgeCollection,
$vertex_current->getId() => [],
];

do {
Expand All @@ -107,7 +106,7 @@ public function getEdgesMap(): array

if (!isset($edges[$vid])) {
$vertex_queue[] = $vertex_target;
$edges[$vid] = $edges[$vertex_current->getId()]->merge([$edge]);
$edges[$vid] = array_merge($edges[$vertex_current->getId()], [$edge]);
}
}

Expand All @@ -124,9 +123,9 @@ public function getEdgesMap(): array
*
* @throws OutOfBoundsException
*
* @return \PHGraph\Support\EdgeCollection<\PHGraph\Edge>
* @return \PHGraph\Edge[]
*/
public function getEdgesTo(Vertex $vertex): EdgeCollection
public function getEdgesTo(Vertex $vertex): array
{
if ($vertex->getGraph() !== $this->vertex->getGraph()) {
throw new OutOfBoundsException;
Expand Down Expand Up @@ -156,15 +155,14 @@ public function getDistanceMap(): array
/**
* Get all the edges that were mapped out.
*
* @return \PHGraph\Support\EdgeCollection<\PHGraph\Edge>
* @return \PHGraph\Edge[]
*/
public function getEdges(): EdgeCollection
public function getEdges(): array
{
$all_edges = new EdgeCollection;
$all_edges = [];

/** @var \PHGraph\Support\EdgeCollection $edges */
foreach ($this->getEdgesMap() as $edges) {
$all_edges = $all_edges->merge($edges);
$all_edges = array_merge($all_edges, $edges);
}

return $all_edges;
Expand Down
56 changes: 32 additions & 24 deletions src/ShortestPath/Dijkstra.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,9 @@
use PHGraph\Contracts\ShortestPath;
use PHGraph\Edge;
use PHGraph\Graph;
use PHGraph\Support\EdgeCollection;
use PHGraph\Support\VertexCollection;
use PHGraph\Vertex;
use PHGraph\Walk;
use SplObjectStorage;
use SplPriorityQueue;
use UnexpectedValueException;

Expand Down Expand Up @@ -99,7 +98,9 @@ public function createGraph(): Graph
*/
public function getDistance(Vertex $vertex): float
{
return $this->getEdgesTo($vertex)->sumAttribute('weight');
return array_sum(array_map(function ($vertex) {
return $vertex->getAttribute('weight', 0);
}, $this->getEdgesTo($vertex)));
}

/**
Expand All @@ -109,12 +110,12 @@ public function getDistance(Vertex $vertex): float
*
* @throws OutOfBoundsException
*
* @return \PHGraph\Support\EdgeCollection<\PHGraph\Edge>
* @return \PHGraph\Edge[]
*/
public function getEdgesTo(Vertex $vertex): EdgeCollection
public function getEdgesTo(Vertex $vertex): array
{
$current_vertex = $vertex;
$path = new EdgeCollection;
$path = [];

if ($vertex === $this->vertex) {
return $path;
Expand All @@ -127,19 +128,19 @@ public function getEdgesTo(Vertex $vertex): EdgeCollection

/** @var \PHGraph\Edge $edge */
foreach ($edges as $edge) {
if ($path->contains($edge)) {
if ($path[$edge->getId()] ?? false) {
continue;
}

if (!$edge->isDirected() && $edge->getFrom() === $current_vertex) {
$path->add($edge);
$path[$edge->getId()] = $edge;
$pre = $edge->getTo();

break;
}

if ($edge->getTo() === $current_vertex) {
$path->add($edge);
$path[$edge->getId()] = $edge;
$pre = $edge->getFrom();

break;
Expand All @@ -153,7 +154,7 @@ public function getEdgesTo(Vertex $vertex): EdgeCollection
$current_vertex = $pre;
} while ($current_vertex !== $this->vertex);

return $path->reverse();
return array_reverse($path);
}

/**
Expand All @@ -166,7 +167,7 @@ public function getDistanceMap(): array
$ret = [];
foreach ($this->vertex->getGraph()->getVertices() as $vertex) {
try {
$ret[$vertex->getId()] = $this->getEdgesTo($vertex)->sumAttribute('weight');
$ret[$vertex->getId()] = $this->getDistance($vertex);
} catch (OutOfBoundsException $ignore) {
// ignore vertices that can not be reached
}
Expand All @@ -180,9 +181,9 @@ public function getDistanceMap(): array
*
* @throws UnexpectedValueException
*
* @return \PHGraph\Support\EdgeCollection<\PHGraph\Edge>
* @return \PHGraph\Edge[]
*/
public function getEdges(): EdgeCollection
public function getEdges(): array
{
if ($this->edges !== null) {
return $this->edges;
Expand All @@ -202,7 +203,7 @@ public function getEdges(): EdgeCollection
$this->vertex->getId() => $this->vertex,
];

$used_vertices = new VertexCollection;
$used_vertices = new SplObjectStorage;

for ($i = 0; $i < count($vertices); $i++) {
if ($vertex_queue->isEmpty()) {
Expand Down Expand Up @@ -248,25 +249,32 @@ public function getEdges(): EdgeCollection
}
}

$used_vertices->add($current_vertex);
$used_vertices->attach($current_vertex);
}

if ($cost_to[$this->vertex->getId()] === INF) {
unset($lowest_cost_vertex_to[$this->vertex->getId()]);
}

$edges = new EdgeCollection;
$edges = [];
/** @var \PHGraph\Vertex $vertex */
foreach ($vertices as $vid => $vertex) {
if ($lowest_cost_vertex_to[$vid] ?? false) {
/** @var \PHGraph\Edge $closest_edge */
$closest_edge = $lowest_cost_vertex_to[$vid]->getEdgesOut()->filter(function (Edge $edge) use ($vertex) {
return $edge->getTo() === $vertex || (!$edge->isDirected() && $edge->getFrom() === $vertex);
})->sortBy(function (Edge $edge) {
return $edge->getAttribute('weight', 0);
})->first();

$edges[] = $closest_edge;
/** @var \PHGraph\Edge[] $closest_edges */
$closest_edges = array_filter(
$lowest_cost_vertex_to[$vid]->getEdgesOut(),
function (Edge $edge) use ($vertex) {
return $edge->getTo() === $vertex || (!$edge->isDirected() && $edge->getFrom() === $vertex);
}
);

uasort($closest_edges, function ($a, $b) {
return $a->getAttribute('weight', 0) - $b->getAttribute('weight', 0);
});

$closest_edge = end($closest_edges);

$edges[$closest_edge->getId()] = $closest_edge;
}
}

Expand Down
81 changes: 50 additions & 31 deletions src/ShortestPath/MooreBellmanFord.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,9 @@
use PHGraph\Contracts\ShortestPath;
use PHGraph\Exception\NegativeCycleException;
use PHGraph\Graph;
use PHGraph\Support\EdgeCollection;
use PHGraph\Support\VertexCollection;
use PHGraph\Vertex;
use PHGraph\Walk;
use SplObjectStorage;
use UnderflowException;

/**
Expand All @@ -23,7 +22,7 @@ class MooreBellmanFord implements ShortestPath
{
/** @var \PHGraph\Vertex */
protected $vertex;
/** @var \PHGraph\Support\EdgeCollection */
/** @var \PHGraph\Edge[] */
protected $edges;

/**
Expand Down Expand Up @@ -91,7 +90,9 @@ public function createGraph(): Graph
*/
public function getDistance(Vertex $vertex): float
{
return $this->getEdgesTo($vertex)->sumAttribute('weight');
return array_sum(array_map(function ($vertex) {
return $vertex->getAttribute('weight', 0);
}, $this->getEdgesTo($vertex)));
}

/**
Expand All @@ -101,12 +102,12 @@ public function getDistance(Vertex $vertex): float
*
* @throws OutOfBoundsException
*
* @return \PHGraph\Support\EdgeCollection<\PHGraph\Edge>
* @return \PHGraph\Edge[]
*/
public function getEdgesTo(Vertex $vertex): EdgeCollection
public function getEdgesTo(Vertex $vertex): array
{
$current_vertex = $vertex;
$path = new EdgeCollection;
$path = [];

if ($vertex === $this->vertex) {
return $path;
Expand All @@ -119,14 +120,14 @@ public function getEdgesTo(Vertex $vertex): EdgeCollection

foreach ($edges as $edge) {
if (!$edge->isDirected() && $edge->getFrom() === $current_vertex) {
$path->add($edge);
$path[$edge->getId()] = $edge;
$pre = $edge->getTo();

break;
}

if ($edge->getTo() === $current_vertex) {
$path->add($edge);
$path[$edge->getId()] = $edge;
$pre = $edge->getFrom();

break;
Expand All @@ -140,7 +141,7 @@ public function getEdgesTo(Vertex $vertex): EdgeCollection
$current_vertex = $pre;
} while ($current_vertex !== $this->vertex);

return $path->reverse();
return array_reverse($path);
}

/**
Expand All @@ -153,7 +154,7 @@ public function getDistanceMap(): array
$ret = [];
foreach ($this->vertex->getGraph()->getVertices() as $vertex) {
try {
$ret[$vertex->getId()] = $this->getEdgesTo($vertex)->sumAttribute('weight');
$ret[$vertex->getId()] = $this->getDistance($vertex);
} catch (OutOfBoundsException $ignore) {
// ignore vertices that can not be reached
}
Expand All @@ -167,9 +168,9 @@ public function getDistanceMap(): array
*
* @throws \PHGraph\Exception\NegativeCycleException
*
* @return \PHGraph\Support\EdgeCollection<\PHGraph\Edge>
* @return \PHGraph\Edge[]
*/
public function getEdges(): EdgeCollection
public function getEdges(): array
{
if ($this->edges !== null) {
return $this->edges;
Expand Down Expand Up @@ -219,16 +220,24 @@ public function getEdges(): EdgeCollection
unset($lowest_cost_vertex_to[$this->vertex->getId()]);
}

$edges = new EdgeCollection;
$edges = [];
foreach ($vertices as $vid => $vertex) {
if ($lowest_cost_vertex_to[$vid] ?? false) {
$closest_edge = $lowest_cost_vertex_to[$vid]->getEdgesOut()->filter(function ($edge) use ($vertex) {
return $edge->getTo() === $vertex || (!$edge->isDirected() && $edge->getFrom() === $vertex);
})->sortBy(function ($edge) {
return $edge->getAttribute('weight', 0);
})->first();
/** @var \PHGraph\Edge[] $closest_edges */
$closest_edges = array_filter(
$lowest_cost_vertex_to[$vid]->getEdgesOut(),
function ($edge) use ($vertex) {
return $edge->getTo() === $vertex || (!$edge->isDirected() && $edge->getFrom() === $vertex);
}
);

uasort($closest_edges, function ($a, $b) {
return $a->getAttribute('weight', 0) - $b->getAttribute('weight', 0);
});

$edges[] = $closest_edge;
$closest_edge = end($closest_edges);

$edges[$closest_edge->getId()] = $closest_edge;
}
}

Expand All @@ -241,29 +250,39 @@ public function getEdges(): EdgeCollection
$new_cost = $cost_to[$from_vertex->getId()] + $edge->getAttribute('weight', 0);

if (!isset($cost_to[$to_vertex->getId()]) || $cost_to[$to_vertex->getId()] > $new_cost) {
$negative_cycle_vertex = $to_vertex;
$cost_to[$to_vertex->getId()] = $new_cost;
$lowest_cost_vertex_to[$to_vertex->getId()] = $from_vertex;
}
}
}

$edges = new EdgeCollection;
$cycle_vertices = new VertexCollection;
$edges = [];
$cycle_vertices = new SplObjectStorage;
$current_vertex = $change_vertex;
do {
$predecessor_vertex = $lowest_cost_vertex_to[$current_vertex->getId()];
$edges[] = $current_vertex->getEdgesIn()->filter(function ($edge) use ($predecessor_vertex) {
return $edge->getFrom() === $predecessor_vertex
|| (!$edge->isDirected() && $edge->getTo() === $predecessor_vertex);
})->sortBy(function ($edge) {
return $edge->getAttribute('weight', 0);
})->first();
$cycle_vertices[] = $current_vertex;

/** @var \PHGraph\Edge[] $closest_edges */
$closest_edges = array_filter(
$current_vertex->getEdgesIn(),
function ($edge) use ($predecessor_vertex) {
return $edge->getFrom() === $predecessor_vertex
|| (!$edge->isDirected() && $edge->getTo() === $predecessor_vertex);
}
);

uasort($closest_edges, function ($a, $b) {
return $a->getAttribute('weight', 0) - $b->getAttribute('weight', 0);
});

$closest_edge = end($closest_edges);

$edges[$closest_edge->getId()] = $closest_edge;
$cycle_vertices->attach($current_vertex);
$current_vertex = $predecessor_vertex;
} while ($change_vertex !== $current_vertex && !$cycle_vertices->contains($current_vertex));

$cycle = new Walk($change_vertex, $edges->reverse());
$cycle = new Walk($change_vertex, array_reverse($edges));
throw new NegativeCycleException('Negative cycle found', 0, null, $cycle);
}

Expand Down
Loading

0 comments on commit 105a02d

Please sign in to comment.