Satsuma
a delicious .NET graph library
 All Classes Namespaces Files Functions Variables Enumerations Enumerator Properties Pages
Dijsktra.cs
Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.Threading.Tasks;
6 
7 namespace Satsuma
8 {
10  public enum DijkstraMode
11  {
15  Sum,
16 
19  Maximum
20  }
21 
52  public sealed class Dijkstra
53  {
55  public IGraph Graph { get; private set; }
59  public Func<Arc, double> Cost { get; private set; }
60 
62  public DijkstraMode Mode { get; private set; }
66  public double NullCost { get; private set; }
67 
68  private readonly Dictionary<Node, double> distance;
69  private readonly Dictionary<Node, Arc> parentArc;
70  private readonly PriorityQueue<Node, double> priorityQueue;
71 
75  public Dijkstra(IGraph graph, Func<Arc, double> cost, DijkstraMode mode)
76  {
77  Graph = graph;
78  Cost = cost;
79  Mode = mode;
80  NullCost = (mode == DijkstraMode.Sum ? 0 : double.NegativeInfinity);
81 
82  distance = new Dictionary<Node, double>();
83  parentArc = new Dictionary<Node, Arc>();
84  priorityQueue = new PriorityQueue<Node, double>();
85  }
86 
87  private void ValidateCost(double c)
88  {
89  if (Mode == DijkstraMode.Sum && c < 0)
90  throw new InvalidOperationException("Invalid cost: " + c);
91  }
92 
95  public void AddSource(Node node)
96  {
97  AddSource(node, NullCost);
98  }
99 
106  public void AddSource(Node node, double nodeCost)
107  {
108  if (Reached(node)) throw new InvalidOperationException("Cannot add a reached node as a source.");
109  ValidateCost(nodeCost);
110 
111  parentArc[node] = Arc.Invalid;
112  priorityQueue[node] = nodeCost;
113  }
114 
118  public Node Step()
119  {
120  if (priorityQueue.Count == 0) return Node.Invalid;
121 
122  // find the closest reached but unfixed node
123  double minDist;
124  Node min = priorityQueue.Peek(out minDist);
125  priorityQueue.Pop();
126 
127  if (double.IsPositiveInfinity(minDist)) return Node.Invalid;
128  distance[min] = minDist; // fix the node
129 
130  // modify keys for neighboring nodes in the priority queue
131  foreach (var arc in Graph.Arcs(min, ArcFilter.Forward))
132  {
133  Node other = Graph.Other(arc, min);
134  if (Fixed(other)) continue; // already processed
135 
136  double arcCost = Cost(arc);
137  ValidateCost(arcCost);
138  double newDist = (Mode == DijkstraMode.Sum ? minDist + arcCost : Math.Max(minDist, arcCost));
139 
140  double oldDist;
141  if (!priorityQueue.TryGetPriority(other, out oldDist)) oldDist = double.PositiveInfinity;
142 
143  if (newDist < oldDist)
144  {
145  priorityQueue[other] = newDist;
146  parentArc[other] = arc;
147  }
148  }
149 
150  return min;
151  }
152 
154  public void Run()
155  {
156  while (Step() != Node.Invalid) ;
157  }
158 
162  public Node RunUntilFixed(Node target)
163  {
164  if (Fixed(target)) return target; // already fixed
165  while (true)
166  {
167  Node fixedNode = Step();
168  if (fixedNode == Node.Invalid || fixedNode == target) return fixedNode;
169  }
170  }
171 
174  public Node RunUntilFixed(Func<Node, bool> isTarget)
175  {
176  Node fixedNode = FixedNodes.FirstOrDefault(isTarget);
177  if (fixedNode != Node.Invalid) return fixedNode; // already fixed
178  while (true)
179  {
180  fixedNode = Step();
181  if (fixedNode == Node.Invalid || isTarget(fixedNode)) return fixedNode;
182  }
183  }
184 
187  public bool Reached(Node node)
188  {
189  return parentArc.ContainsKey(node);
190  }
191 
194  public IEnumerable<Node> ReachedNodes { get { return parentArc.Keys; } }
195 
204  public bool Fixed(Node node)
205  {
206  return distance.ContainsKey(node);
207  }
208 
211  public IEnumerable<Node> FixedNodes { get { return distance.Keys; } }
212 
216  public double GetDistance(Node node)
217  {
218  double result;
219  return distance.TryGetValue(node, out result) ? result : double.PositiveInfinity;
220  }
221 
224  public Arc GetParentArc(Node node)
225  {
226  Arc result;
227  return parentArc.TryGetValue(node, out result) ? result : Arc.Invalid;
228  }
229 
232  public IPath GetPath(Node node)
233  {
234  if (!Reached(node)) return null;
235 
236  var result = new Path(Graph);
237  result.Begin(node);
238  while (true)
239  {
240  Arc arc = GetParentArc(node);
241  if (arc == Arc.Invalid) break;
242  result.AddFirst(arc);
243  node = Graph.Other(arc, node);
244  }
245  return result;
246  }
247  }
248 }