-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Graphs: Shortest Distance - A* and bidirectional A* (#227)
- Loading branch information
1 parent
5d3f9f1
commit 83a5bf8
Showing
25 changed files
with
470 additions
and
34 deletions.
There are no files selected for viewing
39 changes: 39 additions & 0 deletions
39
MoreStructures.Tests/Graphs/DictionaryAdapterGraphDistancesTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
using MoreStructures.Graphs; | ||
|
||
namespace MoreStructures.Tests.Graphs; | ||
|
||
[TestClass] | ||
public class DictionaryAdapterGraphDistancesTests | ||
{ | ||
[TestMethod] | ||
public void Indexer_TakesDataFromUnderlyingDictionary() | ||
{ | ||
var dictionary = new Dictionary<(int, int), int> | ||
{ | ||
[(0, 0)] = 0, | ||
[(0, 1)] = 1, | ||
[(0, 2)] = 2, | ||
[(1, 0)] = 3, | ||
}; | ||
var graphDistances = new DictionaryAdapterGraphDistances(dictionary); | ||
|
||
foreach (var key in dictionary.Keys) | ||
Assert.AreEqual(dictionary[key], graphDistances[key]); | ||
} | ||
|
||
[TestMethod] | ||
public void Indexer_RaisesExceptionWhenProvidedEdgeIsUnknown() | ||
{ | ||
var dictionary = new Dictionary<(int, int), int> | ||
{ | ||
[(0, 0)] = 0, | ||
[(0, 1)] = 1, | ||
[(0, 2)] = 2, | ||
[(1, 0)] = 3, | ||
}; | ||
var graphDistances = new DictionaryAdapterGraphDistances(dictionary); | ||
|
||
Assert.ThrowsException<KeyNotFoundException>(() => graphDistances[(1, 2)]); | ||
Assert.ThrowsException<KeyNotFoundException>(() => graphDistances[(2, 0)]); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
27 changes: 27 additions & 0 deletions
27
MoreStructures.Tests/Graphs/ShortestDistance/AStarShortestDistanceFinderTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
using MoreStructures.Graphs; | ||
using MoreStructures.Graphs.ShortestDistance; | ||
using MoreStructures.PriorityQueues.BinomialHeap; | ||
|
||
namespace MoreStructures.Tests.Graphs.ShortestDistance; | ||
|
||
[TestClass] | ||
public class AStarShortestDistanceFinderTests_WithoutHeuristic : DijkstraShortestDistanceFinderTests | ||
{ | ||
public AStarShortestDistanceFinderTests_WithoutHeuristic() | ||
: base( | ||
(numberOfVertices, edges) => new EdgeListGraph(numberOfVertices, edges), | ||
() => new AStarShortestDistanceFinder(() => new UpdatableBinomialHeapPriorityQueue<int>())) | ||
{ | ||
} | ||
} | ||
|
||
[TestClass] | ||
public class AStarShortestDistanceFinderTests_WithHeuristic : PotentialBasedShortestDistanceFinderTests | ||
{ | ||
public AStarShortestDistanceFinderTests_WithHeuristic() | ||
: base( | ||
(numberOfVertices, edges) => new EdgeListGraph(numberOfVertices, edges), | ||
() => new AStarShortestDistanceFinder(() => new UpdatableBinomialHeapPriorityQueue<int>())) | ||
{ | ||
} | ||
} |
27 changes: 27 additions & 0 deletions
27
...Structures.Tests/Graphs/ShortestDistance/BidirectionalAStarShortestDistanceFinderTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
using MoreStructures.Graphs; | ||
using MoreStructures.Graphs.ShortestDistance; | ||
using MoreStructures.PriorityQueues.BinomialHeap; | ||
|
||
namespace MoreStructures.Tests.Graphs.ShortestDistance; | ||
|
||
[TestClass] | ||
public class BidirectionalAStarShortestDistanceFinderTests_WithoutHeuristic : DijkstraShortestDistanceFinderTests | ||
{ | ||
public BidirectionalAStarShortestDistanceFinderTests_WithoutHeuristic() | ||
: base( | ||
(numberOfVertices, edges) => new EdgeListGraph(numberOfVertices, edges), | ||
() => new BidirectionalAStarShortestDistanceFinder(() => new UpdatableBinomialHeapPriorityQueue<int>())) | ||
{ | ||
} | ||
} | ||
|
||
[TestClass] | ||
public class BidirectionalAStarShortestDistanceFinderTests_WithHeuristic : PotentialBasedShortestDistanceFinderTests | ||
{ | ||
public BidirectionalAStarShortestDistanceFinderTests_WithHeuristic() | ||
: base( | ||
(numberOfVertices, edges) => new EdgeListGraph(numberOfVertices, edges), | ||
() => new BidirectionalAStarShortestDistanceFinder(() => new UpdatableBinomialHeapPriorityQueue<int>())) | ||
{ | ||
} | ||
} |
File renamed without changes.
56 changes: 56 additions & 0 deletions
56
MoreStructures.Tests/Graphs/ShortestDistance/PotentialBasedShortestDistanceFinderTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
using MoreStructures.Graphs; | ||
using MoreStructures.Graphs.ShortestDistance; | ||
|
||
namespace MoreStructures.Tests.Graphs.ShortestDistance; | ||
|
||
public abstract class PotentialBasedShortestDistanceFinderTests | ||
{ | ||
protected Func<int, IList<(int, int)>, IGraph> GraphBuilder { get; } | ||
protected Func<IPotentialBasedShortestDistanceFinder> FinderBuilder { get; } | ||
|
||
protected PotentialBasedShortestDistanceFinderTests( | ||
Func<int, IList<(int, int)>, IGraph> graphBuilder, Func<IPotentialBasedShortestDistanceFinder> finderBuilder) | ||
{ | ||
GraphBuilder = graphBuilder; | ||
FinderBuilder = finderBuilder; | ||
} | ||
|
||
[DataRow("7 V, source to sink, same source to 1-chain and 3-chain merging to vertex to sink", 7, | ||
new[] { 0, 0, 0, 1, 2, 3, 4, 5 }, | ||
new[] { 1, 2, 6, 3, 4, 6, 5, 1 }, | ||
new[] { 27, 3, 21, 3, 3, 3, 3, 3 }, | ||
new int[] | ||
{ | ||
9, 8, 7, 6, 5, 4, 3, // Bad potentials: non-sensible values | ||
1, 1, 1, 1, 1, 1, 1, // Bad potentials: all equal values | ||
0, 3, 1, 4, 2, 3, 3, // Approx euclidean potentials calculated from vertex 0 | ||
3, 0, 3, 1, 2, 1, 1, // Approx euclidean potentials calculated from vertex 1 | ||
1, 3, 2, 1, 3, 2, 4, // Approx euclidean potentials calculated from vertex 2 | ||
})] | ||
[DataTestMethod] | ||
public void Find_IsCorrect( | ||
string graphDescription, int numberOfVertices, int[] starts, int[] ends, int[] distances, int[] potentials) | ||
{ | ||
var graph = GraphBuilder(numberOfVertices, starts.Zip(ends).ToList()); | ||
var graphDistances = new DictionaryAdapterGraphDistances( | ||
starts.Zip(ends).Zip(distances).ToDictionary(t => t.First, t => t.Second)); | ||
|
||
var finder = FinderBuilder(); | ||
|
||
for (var start = 0; start < numberOfVertices; start++) | ||
{ | ||
for (var end = 0; end < numberOfVertices; end++) | ||
{ | ||
for (var i = 0; i < potentials.Length; i += numberOfVertices) | ||
{ | ||
var (distanceWithHeuristic, pathWithHeuristic) = | ||
finder.Find(graph, graphDistances, v => potentials[i + v], start, end); | ||
var (distanceWithoutHeuristic, pathWithoutHeuristic) = | ||
finder.Find(graph, graphDistances, start, end); | ||
Assert.AreEqual(distanceWithoutHeuristic, distanceWithHeuristic, graphDescription); | ||
Assert.IsTrue(pathWithoutHeuristic.SequenceEqual(pathWithHeuristic), graphDescription); | ||
} | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
namespace MoreStructures.Graphs; | ||
|
||
/// <summary> | ||
/// A <see cref="IGraphDistances"/> retrieving distances from a <see cref="IDictionary{TKey, TValue}"/>, mapping | ||
/// couples of <see cref="int"/> values (ids of endpoints of each edge of the graph) to <see cref="int"/> values | ||
/// (edge distances). | ||
/// </summary> | ||
public class DictionaryAdapterGraphDistances : IGraphDistances | ||
{ | ||
private IDictionary<(int, int), int> Dictionary { get; } | ||
|
||
/// <inheritdoc path="//*[not(self::remarks)]"/> | ||
/// <remarks> | ||
/// Retrieves the value from the underlying dictionary. | ||
/// </remarks> | ||
public int this[(int edgeStart, int edgeEnd) edge] => Dictionary[edge]; | ||
|
||
/// <summary> | ||
/// <inheritdoc cref="DictionaryAdapterGraphDistances"/> | ||
/// </summary> | ||
/// <param name="dictionary">The mapping between edges and distances.</param> | ||
public DictionaryAdapterGraphDistances(IDictionary<(int, int), int> dictionary) | ||
{ | ||
Dictionary = dictionary; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
namespace MoreStructures.Graphs; | ||
|
||
/// <summary> | ||
/// Represents a mapping between edges of a <see cref="IGraph"/> and distances, in a spatial context, or weights, in | ||
/// a more general setting. | ||
/// </summary> | ||
public interface IGraphDistances | ||
{ | ||
/// <summary> | ||
/// Returns the distance, or weight, of the provided <paramref name="edge"/>. | ||
/// </summary> | ||
/// <param name="edge">The edge, to provide the distance of.</param> | ||
/// <returns>Any positive or negative number.</returns> | ||
int this[(int edgeStart, int edgeEnd) edge] { get; } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
78 changes: 78 additions & 0 deletions
78
MoreStructures/Graphs/ShortestDistance/AStarShortestDistanceFinder.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
using MoreStructures.PriorityQueues; | ||
|
||
namespace MoreStructures.Graphs.ShortestDistance; | ||
|
||
/// <summary> | ||
/// A <see cref="IShortestDistanceFinder"/> implementation based on the A* algorithm, which is a refinement of | ||
/// the Dijkstra's algorithm, introducing a goal-oriented heuristic, driving the search in the direction of the end | ||
/// vertex. | ||
/// </summary> | ||
/// <remarks> | ||
/// <para id="requirements"> | ||
/// <inheritdoc cref="DijkstraShortestDistanceFinder" path="/remarks/para[@id='requirements']"/> | ||
/// </para> | ||
/// <para id="algorithm"> | ||
/// ALGORITHM | ||
/// <br/> | ||
/// - The algorithm is described in <see cref="DijkstraShortestDistanceFinder"/>, with the only difference that | ||
/// edge distances are modified, based on a heuristic defined as a potential function. | ||
/// <br/> | ||
/// - New edge distance is defined as follow: given the potential function P, for each edge (u, v) in the graph, | ||
/// <c>d'(u, v) = d(u, v) + P(v) - P(u)</c>. | ||
/// <br/> | ||
/// - If P is defined correctly, P(u) and P(v) are good approximations of the distance of u and v from the end | ||
/// vertex e. | ||
/// <br/> | ||
/// - If so, <c>P(v) - P(u)</c> will be negative if moving from u to v gets us closer to e and positive if it | ||
/// gets us farther from it. | ||
/// <br/> | ||
/// - For this reason, given two vertices v' and v'' connected from u via <c>e' = (u, v')</c> and | ||
/// <c>e'' = (u, v'')</c>, and such that <c>d(e') = d(e'')</c>, if <c>P(v') < P(v'')</c> then | ||
/// <c>d'(e') < d''(e')</c>, so the algorithm will prefer e' over e'' during the exploration, as it seems to | ||
/// be closer to e. | ||
/// <br/> | ||
/// - Because the algorithm stops when e is processed, if the algorithm visits e earlier than later, the algorithm | ||
/// will find the shortest path from s to e ealier than later. | ||
/// </para> | ||
/// <para id="complexity"> | ||
/// COMPLEXITY | ||
/// <br/> | ||
/// - The complexity heavily depends on the accuracy of the potential function. | ||
/// <br/> | ||
/// - A good model of the average performance of the algorithm is very complicated to derive, since the heuristic | ||
/// can drive the exploration much quicker or slower towards the end vertex, depending on how the function is | ||
/// defined. | ||
/// <br/> | ||
/// - In general, potential functions which are closer to the actual shortest distance to the end vertex yield | ||
/// better results. The farther they move from the ideal, the less optimized the exploration of the graph | ||
/// becomes. | ||
/// <br/> | ||
/// - Worst case remains as in <see cref="DijkstraShortestDistanceFinder"/>, where all vertices of the graph have | ||
/// to be explored, for a path from the start to the end to be found (or prove there is no path, since start and | ||
/// end lie in two different connected components). | ||
/// <br/> | ||
/// - Best case is when P is the shortest distance to e, in which case only the vertices of a shortest path from | ||
/// s to e are visited (which is tipically a small fraction of the vertices of the graph, especially if the graph | ||
/// is large). That is the bare minimum to find the shortest path from s to e. | ||
/// <br/> | ||
/// - Average case can even be worse than normal Dijkstra, if P is misleading, i.e. if it drives the exploration | ||
/// away from e, rather than reducing the cost of edges which drives the exploration closer to e. | ||
/// <br/> | ||
/// - However, with a well defined P, close enough to the actual shortest distance, Time Complexity is between | ||
/// O(e + v * log(v)) and O(h), where h is the highest number of edges of a shortest path from s to e. | ||
/// <br/> | ||
/// - Space Complexity is also between O(h) and O(v). | ||
/// </para> | ||
/// </remarks> | ||
public class AStarShortestDistanceFinder : PotentialBasedShortestDistanceFinder | ||
{ | ||
/// <inheritdoc cref="AStarShortestDistanceFinder"/> | ||
/// <param name="priorityQueueBuilder"> | ||
/// A builder of a <see cref="IUpdatablePriorityQueue{T}"/> of <see cref="int"/> values, used by the algorithm to | ||
/// store edges with priority from the closest to the start, to the farthest. | ||
/// </param> | ||
public AStarShortestDistanceFinder(Func<IUpdatablePriorityQueue<int>> priorityQueueBuilder) | ||
: base(new DijkstraShortestDistanceFinder(priorityQueueBuilder)) | ||
{ | ||
} | ||
} |
Oops, something went wrong.