Skip to content

Commit

Permalink
Update documentation with GetShortestPaths
Browse files Browse the repository at this point in the history
  • Loading branch information
viceroypenguin committed Oct 12, 2023
1 parent d2a69d1 commit ac14a31
Show file tree
Hide file tree
Showing 4 changed files with 190 additions and 79 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
uid: SuperLinq.SuperEnumerable.GetShortestPaths``2(``0,System.Func{``0,``1,System.Collections.Generic.IEnumerable{System.ValueTuple{``0,``1}}})
example: [*content]
---
The following code example demonstrates how to use Dijkstra's algorithm to build a distance map using `GetShortestPaths`.
[!code-csharp[](SuperLinq/GetShortestPaths/GetShortestPaths1.linq#L6-)]

---
uid: SuperLinq.SuperEnumerable.GetShortestPaths``2(``0,System.Func{``0,``1,System.Collections.Generic.IEnumerable{System.ValueTuple{``0,``1}}},System.Collections.Generic.IEqualityComparer{``0},System.Collections.Generic.IComparer{``1})
example: [*content]
---
The following code example demonstrates how to use Dijkstra's algorithm to build a distance map using `GetShortestPaths`.
[!code-csharp[](SuperLinq/GetShortestPaths/GetShortestPaths2.linq#L6-)]
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<Query Kind="Statements">
<NuGetReference>SuperLinq</NuGetReference>
<Namespace>SuperLinq</Namespace>
</Query>

var costs =
new[]
{
(from: "start", to: "a", cost: 1),
(from: "a", to: "b", cost: 2),
(from: "b", to: "c", cost: 3),
(from: "c", to: "d", cost: 4),
(from: "d", to: "end", cost: 5),
(from: "start", to: "A", cost: 10),
(from: "A", to: "B", cost: 20),
(from: "B", to: "C", cost: 30),
(from: "C", to: "D", cost: 40),
(from: "D", to: "end", cost: 50),
(from: "start", to: "END", cost: 10),
(from: "start", to: "END", cost: 1000),
};
var map = costs
.Concat(costs.Select(x => (from: x.to, to: x.from, x.cost)))
.Where(x =>
x.to != "start"
&& x.from != "end")
.ToLookup(x => x.from, x => (x.to, x.cost));

// Find the shortest path from start to end
var result = SuperEnumerable
.GetShortestPaths<string, int>(
"start",
(state, cost) => map[state]
.Select(x => (x.to, x.cost + cost)));

foreach (var (key, (from, cost)) in result)
{
Console.WriteLine($"[{key}] = (from: {from}, totalCost: {cost})");
}

// This code produces the following output:
// [start] = (from: , totalCost: 0)
// [a] = (from: start, totalCost: 1)
// [b] = (from: a, totalCost: 3)
// [c] = (from: b, totalCost: 6)
// [END] = (from: start, totalCost: 10)
// [d] = (from: c, totalCost: 10)
// [A] = (from: start, totalCost: 10)
// [end] = (from: d, totalCost: 15)
// [B] = (from: A, totalCost: 30)
// [C] = (from: B, totalCost: 60)
// [D] = (from: C, totalCost: 100)
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<Query Kind="Statements">
<NuGetReference>SuperLinq</NuGetReference>
<Namespace>SuperLinq</Namespace>
</Query>

var costs =
new[]
{
(from: "start", to: "a", cost: 1),
(from: "a", to: "b", cost: 2),
(from: "b", to: "c", cost: 3),
(from: "c", to: "d", cost: 4),
(from: "d", to: "end", cost: 5),
(from: "start", to: "A", cost: 10),
(from: "A", to: "B", cost: 20),
(from: "B", to: "C", cost: 30),
(from: "C", to: "D", cost: 40),
(from: "D", to: "end", cost: 50),
(from: "start", to: "END", cost: 10),
(from: "start", to: "END", cost: 1000),
};
var map = costs
.Concat(costs.Select(x => (from: x.to, to: x.from, x.cost)))
.Where(x =>
x.to != "start"
&& x.from != "end")
.ToLookup(x => x.from, x => (x.to, x.cost));

// Find the shortest path from start to end
var result = SuperEnumerable
.GetShortestPaths<string, int>(
"start",
(state, cost) => map[state]
.Select(x => (x.to, x.cost + cost)),
StringComparer.OrdinalIgnoreCase,
default);

foreach (var (key, (from, cost)) in result)
{
Console.WriteLine($"[{key}] = (from: {from}, totalCost: {cost})");
}

// This code produces the following output:
// [start] = (from: , totalCost: 0)
// [a] = (from: start, totalCost: 1)
// [b] = (from: a, totalCost: 3)
// [c] = (from: b, totalCost: 6)
// [END] = (from: start, totalCost: 10)
// [d] = (from: c, totalCost: 10)
155 changes: 76 additions & 79 deletions Source/SuperLinq/GetShortestPaths.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,64 +5,60 @@ namespace SuperLinq;
public partial class SuperEnumerable
{
/// <summary>
/// Find the shortest path from state <paramref name="start"/>
/// to every other <typeparamref name="TState"/> in the map,
/// using Dijkstra's algorithm.
/// Find the shortest path from state <paramref name="start"/> to every other <typeparamref name="TState"/> in
/// the map, using Dijkstra's algorithm.
/// </summary>
/// <typeparam name="TState">The type of each state in the map</typeparam>
/// <typeparam name="TCost">The type of the cost to traverse between states</typeparam>
/// <param name="start">The starting state</param>
/// <typeparam name="TState">
/// The type of each state in the map
/// </typeparam>
/// <typeparam name="TCost">
/// The type of the cost to traverse between states
/// </typeparam>
/// <param name="start">
/// The starting state
/// </param>
/// <param name="getNeighbors">
/// A function that returns the neighbors for a given state
/// and the total cost to get to that state based on the
/// traversal cost at the current state.
/// A function that returns the neighbors for a given state and the total cost to get to that state based on the
/// traversal cost at the current state.
/// </param>
/// <returns>
/// A map that contains, for every <typeparamref name="TState"/>,
/// the previous <typeparamref name="TState"/> in the shortest path
/// from <paramref name="start"/> to this <typeparamref name="TState"/>,
/// as well as the total cost to travel from <paramref name="start"/>
/// to this <typeparamref name="TState"/>.
/// A map that contains, for every <typeparamref name="TState"/>, the previous <typeparamref name="TState"/> in
/// the shortest path from <paramref name="start"/> to this <typeparamref name="TState"/>, as well as the total
/// cost to travel from <paramref name="start"/> to this <typeparamref name="TState"/>.
/// </returns>
/// <exception cref="ArgumentNullException"><paramref name="getNeighbors"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentNullException">
/// <paramref name="getNeighbors"/> is <see langword="null"/>.
/// </exception>
/// <remarks>
/// <para>
/// This method uses Dijkstra's algorithm to explore a map
/// and find the shortest path from <paramref name="start"/>
/// to every other <typeparamref name="TState"/> in the map.
/// An <see cref="UpdatablePriorityQueue{TElement, TPriority}"/>
/// is used to manage the list of <typeparamref name="TState"/>s
/// to process, to reduce the computation cost of this operator.
/// This method uses Dijkstra's algorithm to explore a map and find the shortest path from <paramref
/// name="start"/> to every other <typeparamref name="TState"/> in the map. An <see
/// cref="UpdatablePriorityQueue{TElement, TPriority}"/> is used to manage the list of <typeparamref
/// name="TState"/>s to process, to reduce the computation cost of this operator.
/// </para>
/// <para>
/// Loops and cycles are automatically detected and handled
/// correctly by this operator; only the cheapest path to
/// a given <typeparamref name="TState"/> is used, and other
/// paths (including loops) are discarded.
/// Loops and cycles are automatically detected and handled correctly by this operator; only the cheapest path
/// to a given <typeparamref name="TState"/> is used, and other paths (including loops) are discarded.
/// </para>
/// <para>
/// While <see cref="GetShortestPathCost{TState, TCost}(TState, Func{TState, TCost, IEnumerable{ValueTuple{TState, TCost}}}, TState)"/>
/// and <see cref="GetShortestPath{TState, TCost}(TState, Func{TState, TCost, IEnumerable{ValueTuple{TState, TCost}}}, TState)"/>
/// will work work on infinite maps, this method
/// will execute an infinite loop on infinite maps. This is because
/// this method will attemp to visit every point in the map.
/// This method will terminate only when any points returned by
/// <paramref name="getNeighbors"/> have all already been visited.
/// While <see cref="GetShortestPathCost{TState, TCost}(TState, Func{TState, TCost,
/// IEnumerable{ValueTuple{TState, TCost}}}, TState)"/> and <see cref="GetShortestPath{TState, TCost}(TState,
/// Func{TState, TCost, IEnumerable{ValueTuple{TState, TCost}}}, TState)"/> will work work on infinite maps,
/// this method will execute an infinite loop on infinite maps. This is because this method will attempt to
/// visit every point in the map. This method will terminate only when any points returned by <paramref
/// name="getNeighbors"/> have all already been visited.
/// </para>
/// <para>
/// Dijkstra's algorithm assumes that all costs are positive,
/// that is to say, that it is not possible to go a negative
/// distance from one state to the next. Violating this assumption
/// will have undefined behavior.
/// Dijkstra's algorithm assumes that all costs are positive, that is to say, that it is not possible to go a
/// negative distance from one state to the next. Violating this assumption will have undefined behavior.
/// </para>
/// <para>
/// This method uses <see cref="EqualityComparer{T}.Default"/>
/// to compare <typeparamref name="TState"/>s and
/// <see cref="Comparer{T}.Default"/> to compare traversal
/// This method uses <see cref="EqualityComparer{T}.Default"/> to compare <typeparamref name="TState"/>s and
/// <see cref="Comparer{T}.Default"/> to compare traversal
/// <typeparamref name="TCost"/>s.
/// </para>
/// <para>
/// This operator executes immediately.
/// This operator executes immediately.
/// </para>
/// </remarks>
public static IReadOnlyDictionary<TState, (TState? previousState, TCost? cost)>
Expand All @@ -80,60 +76,61 @@ public partial class SuperEnumerable
}

/// <summary>
/// Find the shortest path from state <paramref name="start"/>
/// to every other <typeparamref name="TState"/> in the map,
/// using Dijkstra's algorithm.
/// Find the shortest path from state <paramref name="start"/> to every other <typeparamref name="TState"/> in
/// the map, using Dijkstra's algorithm.
/// </summary>
/// <typeparam name="TState">The type of each state in the map</typeparam>
/// <typeparam name="TCost">The type of the cost to traverse between states</typeparam>
/// <param name="start">The starting state</param>
/// <typeparam name="TState">
/// The type of each state in the map
/// </typeparam>
/// <typeparam name="TCost">
/// The type of the cost to traverse between states
/// </typeparam>
/// <param name="start">
/// The starting state
/// </param>
/// <param name="getNeighbors">
/// A function that returns the neighbors for a given state
/// and the total cost to get to that state based on the
/// traversal cost at the current state.
/// A function that returns the neighbors for a given state and the total cost to get to that state based on the
/// traversal cost at the current state.
/// </param>
/// <param name="stateComparer">
/// A custom equality comparer for <typeparamref name="TState"/>
/// </param>
/// <param name="stateComparer">A custom equality comparer for <typeparamref name="TState"/></param>
/// <param name="costComparer">A custom comparer for <typeparamref name="TCost"/></param>
/// <param name="costComparer">
/// A custom comparer for <typeparamref name="TCost"/>
/// </param>
/// <returns>
/// A map that contains, for every <typeparamref name="TState"/>,
/// the previous <typeparamref name="TState"/> in the shortest path
/// from <paramref name="start"/> to this <typeparamref name="TState"/>,
/// as well as the total cost to travel from <paramref name="start"/>
/// to this <typeparamref name="TState"/>.
/// A map that contains, for every <typeparamref name="TState"/>, the previous <typeparamref name="TState"/> in
/// the shortest path from <paramref name="start"/> to this <typeparamref name="TState"/>, as well as the total
/// cost to travel from <paramref name="start"/> to this <typeparamref name="TState"/>.
/// </returns>
/// <exception cref="ArgumentNullException"><paramref name="getNeighbors"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentNullException">
/// <paramref name="getNeighbors"/> is <see langword="null"/>.
/// </exception>
/// <remarks>
/// <para>
/// This method uses Dijkstra's algorithm to explore a map
/// and find the shortest path from <paramref name="start"/>
/// to every other <typeparamref name="TState"/> in the map.
/// An <see cref="UpdatablePriorityQueue{TElement, TPriority}"/>
/// is used to manage the list of <typeparamref name="TState"/>s
/// to process, to reduce the computation cost of this operator.
/// This method uses Dijkstra's algorithm to explore a map and find the shortest path from <paramref
/// name="start"/> to every other <typeparamref name="TState"/> in the map. An <see
/// cref="UpdatablePriorityQueue{TElement, TPriority}"/> is used to manage the list of <typeparamref
/// name="TState"/>s to process, to reduce the computation cost of this operator.
/// </para>
/// <para>
/// Loops and cycles are automatically detected and handled
/// correctly by this operator; only the cheapest path to
/// a given <typeparamref name="TState"/> is used, and other
/// paths (including loops) are discarded.
/// Loops and cycles are automatically detected and handled correctly by this operator; only the cheapest path
/// to a given <typeparamref name="TState"/> is used, and other paths (including loops) are discarded.
/// </para>
/// <para>
/// While <see cref="GetShortestPathCost{TState, TCost}(TState, Func{TState, TCost, IEnumerable{ValueTuple{TState, TCost}}}, TState)"/>
/// and <see cref="GetShortestPath{TState, TCost}(TState, Func{TState, TCost, IEnumerable{ValueTuple{TState, TCost}}}, TState)"/>
/// will work work on infinite maps, this method
/// will execute an infinite loop on infinite maps. This is because
/// this method will attemp to visit every point in the map.
/// This method will terminate only when any points returned by
/// <paramref name="getNeighbors"/> have all already been visited.
/// While <see cref="GetShortestPathCost{TState, TCost}(TState, Func{TState, TCost,
/// IEnumerable{ValueTuple{TState, TCost}}}, TState)"/> and <see cref="GetShortestPath{TState, TCost}(TState,
/// Func{TState, TCost, IEnumerable{ValueTuple{TState, TCost}}}, TState)"/> will work work on infinite maps,
/// this method will execute an infinite loop on infinite maps. This is because this method will attempt to
/// visit every point in the map. This method will terminate only when any points returned by <paramref
/// name="getNeighbors"/> have all already been visited.
/// </para>
/// <para>
/// Dijkstra's algorithm assumes that all costs are positive,
/// that is to say, that it is not possible to go a negative
/// distance from one state to the next. Violating this assumption
/// will have undefined behavior.
/// Dijkstra's algorithm assumes that all costs are positive, that is to say, that it is not possible to go a
/// negative distance from one state to the next. Violating this assumption will have undefined behavior.
/// </para>
/// <para>
/// This operator executes immediately.
/// This operator executes immediately.
/// </para>
/// </remarks>
public static IReadOnlyDictionary<TState, (TState? previousState, TCost? cost)>
Expand Down

0 comments on commit ac14a31

Please sign in to comment.