Satsuma
a delicious .NET graph library
 All Classes Namespaces Files Functions Variables Enumerations Enumerator Properties Pages
Tsp.cs
Go to the documentation of this file.
1 #region License
2 /*This file is part of Satsuma Graph Library
3 Copyright © 2013 Balázs Szalkai
4 
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8 
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12 
13  1. The origin of this software must not be misrepresented; you must not
14  claim that you wrote the original software. If you use this software
15  in a product, an acknowledgment in the product documentation would be
16  appreciated but is not required.
17 
18  2. Altered source versions must be plainly marked as such, and must not be
19  misrepresented as being the original software.
20 
21  3. This notice may not be removed or altered from any source
22  distribution.*/
23 #endregion
24 
25 using System;
26 using System.Collections.Generic;
27 using System.Linq;
28 
29 namespace Satsuma
30 {
32  public static class TspUtils
33  {
38  public static double GetTourCost<TNode>(IEnumerable<TNode> tour, Func<TNode, TNode, double> cost)
39  {
40  double result = 0;
41  if (tour.Any())
42  {
43  TNode prev = tour.First();
44  foreach (var node in tour.Skip(1))
45  {
46  result += cost(prev, node);
47  prev = node;
48  }
49  }
50  return result;
51  }
52  }
53 
56  public interface ITsp<TNode>
57  {
60  IEnumerable<TNode> Tour { get; }
62  double TourCost { get; }
63  }
64 
71  public sealed class CheapestLinkTsp<TNode> : ITsp<TNode>
72  {
75  public IList<TNode> Nodes { get; private set; }
77  public Func<TNode, TNode, double> Cost { get; private set; }
78 
79  private List<TNode> tour;
80  public IEnumerable<TNode> Tour { get { return tour; } }
81  public double TourCost { get; private set; }
82 
83  public CheapestLinkTsp(IList<TNode> nodes, Func<TNode, TNode, double> cost)
84  {
85  Nodes = nodes;
86  Cost = cost;
87  tour = new List<TNode>();
88 
89  Run();
90  }
91 
92  private void Run()
93  {
94  // create a complete graph and run Kruskal with maximum degree constraint 2
95  CompleteGraph graph = new CompleteGraph(Nodes.Count, Directedness.Undirected);
96  Func<Arc, double> arcCost = (arc => Cost(Nodes[graph.GetNodeIndex(graph.U(arc))],
97  Nodes[graph.GetNodeIndex(graph.V(arc))]));
98  Kruskal<double> kruskal = new Kruskal<double>(graph, arcCost, _ => 2);
99  kruskal.Run();
100 
101  Dictionary<Node, Arc> firstArc = new Dictionary<Node, Arc>();
102  Dictionary<Node, Arc> secondArc = new Dictionary<Node, Arc>();
103  foreach (var arc in kruskal.Forest)
104  {
105  var u = graph.U(arc);
106  (firstArc.ContainsKey(u) ? secondArc : firstArc)[u] = arc;
107  var v = graph.V(arc);
108  (firstArc.ContainsKey(v) ? secondArc : firstArc)[v] = arc;
109  }
110 
111  foreach (var startNode in graph.Nodes())
112  {
113  if (kruskal.Degree[startNode] == 1)
114  {
115  Arc prevArc = Arc.Invalid;
116  Node n = startNode;
117  while (true)
118  {
119  tour.Add(Nodes[graph.GetNodeIndex(n)]);
120  if (prevArc != Arc.Invalid && kruskal.Degree[n] == 1) break;
121  Arc arc1 = firstArc[n];
122  prevArc = (arc1 != prevArc ? arc1 : secondArc[n]);
123  n = graph.Other(prevArc, n);
124  }
125  tour.Add(Nodes[graph.GetNodeIndex(startNode)]);
126  break;
127  }
128  }
129 
130  TourCost = TspUtils.GetTourCost(tour, Cost);
131  }
132  }
133 
135  public enum TspSelectionRule
136  {
138  Nearest,
140  Farthest
141  }
142 
148  public sealed class InsertionTsp<TNode> : ITsp<TNode>
149  {
151  public IEnumerable<TNode> Nodes { get; private set; }
153  public Func<TNode, TNode, double> Cost { get; private set; }
155  public TspSelectionRule SelectionRule { get; private set; }
156 
157  private LinkedList<TNode> tour;
159  private Dictionary<TNode, LinkedListNode<TNode>> tourNodes;
161  private HashSet<TNode> insertableNodes;
163  private PriorityQueue<TNode, double> insertableNodeQueue;
164 
165  // \copydoc ITsp<TNode>.Tour TODO this does not work in doxygen yet
169  public IEnumerable<TNode> Tour { get { return tour; } }
170  public double TourCost { get; private set; }
171 
172  public InsertionTsp(IEnumerable<TNode> nodes, Func<TNode, TNode, double> cost,
173  TspSelectionRule selectionRule = TspSelectionRule.Farthest)
174  {
175  Nodes = nodes;
176  Cost = cost;
177  SelectionRule = selectionRule;
178 
179  tour = new LinkedList<TNode>();
180  tourNodes = new Dictionary<TNode,LinkedListNode<TNode>>();
181  insertableNodes = new HashSet<TNode>();
182  insertableNodeQueue = new PriorityQueue<TNode, double>();
183 
184  Clear();
185  }
186 
187  private double PriorityFromCost(double c)
188  {
189  switch (SelectionRule)
190  {
191  case TspSelectionRule.Farthest: return -c;
192  default: return c;
193  }
194  }
195 
197  public void Clear()
198  {
199  tour.Clear();
200  TourCost = 0;
201  tourNodes.Clear();
202  insertableNodes.Clear();
203  insertableNodeQueue.Clear();
204 
205  if (Nodes.Any())
206  {
207  TNode startNode = Nodes.First();
208  tour.AddFirst(startNode);
209  tourNodes[startNode] = tour.AddFirst(startNode);
210  foreach (var node in Nodes)
211  if (!node.Equals(startNode))
212  {
213  insertableNodes.Add(node);
214  insertableNodeQueue[node] = PriorityFromCost(Cost(startNode, node));
215  }
216  }
217  }
218 
221  public bool Insert(TNode node)
222  {
223  if (!insertableNodes.Contains(node)) return false;
224  insertableNodes.Remove(node);
225  insertableNodeQueue.Remove(node);
226 
227  // find the optimal insertion place
228  LinkedListNode<TNode> insertAfter = null;
229  double bestIncrease = double.PositiveInfinity;
230  for (var llnode = tour.First; llnode != tour.Last; llnode = llnode.Next)
231  {
232  var llnode2 = llnode.Next;
233  double increase = Cost(llnode.Value, node) + Cost(node, llnode2.Value);
234  if (llnode != llnode2) increase -= Cost(llnode.Value, llnode2.Value);
235  if (increase < bestIncrease)
236  {
237  bestIncrease = increase;
238  insertAfter = llnode;
239  }
240  }
241 
242  LinkedListNode<TNode> llnodeNew = tourNodes[node] = tour.AddAfter(insertAfter, node);
243  TourCost += bestIncrease;
244 
245  // update distances
246  foreach (var n in insertableNodes)
247  {
248  double newPriority = PriorityFromCost(Cost(node, n));
249  if (newPriority < insertableNodeQueue[n]) insertableNodeQueue[n] = newPriority;
250  }
251 
252  return true;
253  }
254 
257  public bool Insert()
258  {
259  if (insertableNodes.Count == 0) return false;
260  Insert(insertableNodeQueue.Peek());
261  return true;
262  }
263 
265  public void Run()
266  {
267  while (Insert()) ;
268  }
269  }
270 
276  public sealed class Opt2Tsp<TNode> : ITsp<TNode>
277  {
279  public Func<TNode, TNode, double> Cost { get; private set; }
280 
281  private List<TNode> tour;
282 
283  public IEnumerable<TNode> Tour { get { return tour; } }
284  public double TourCost { get; private set; }
285 
291  public Opt2Tsp(Func<TNode, TNode, double> cost, IEnumerable<TNode> tour, double? tourCost)
292  {
293  Cost = cost;
294  this.tour = tour.ToList();
295  TourCost = tourCost ?? TspUtils.GetTourCost(tour, cost);
296  }
297 
300  public bool Step()
301  {
302  bool improved = false;
303  for (int i = 0; i < tour.Count-3; i++) // first arc
304  {
305  for (int j = i + 2, jmax = tour.Count - (i == 0 ? 2 : 1); j < jmax; j++) // second arc
306  {
307  double increase = Cost(tour[i], tour[j]) + Cost(tour[i+1], tour[j+1]) -
308  (Cost(tour[i], tour[i+1]) + Cost(tour[j], tour[j+1]));
309  if (increase < 0)
310  {
311  TourCost += increase;
312  tour.Reverse(i + 1, j - i);
313  improved = true;
314  }
315  }
316  }
317  return improved;
318  }
319 
321  public void Run()
322  {
323  while (Step()) ;
324  }
325  }
326 
334  public sealed class HamiltonianCycle
335  {
337  public IGraph Graph { get; private set; }
341  public IPath Cycle { get; private set; }
342 
343  public HamiltonianCycle(IGraph graph)
344  {
345  Graph = graph;
346  Cycle = null;
347 
348  Run();
349  }
350 
351  private void Run()
352  {
353  Func<Node, Node, double> cost = (u, v) => (Graph.Arcs(u, v, ArcFilter.Forward).Any() ? 1 : 10);
354  IEnumerable<Node> tour = null;
355  double minimumTourCost = Graph.NodeCount();
356 
357  // Use the insertion tsp combined with 2-OPT heuristic.
358  InsertionTsp<Node> insertionTsp = new InsertionTsp<Node>(Graph.Nodes(), cost);
359  insertionTsp.Run();
360  if (insertionTsp.TourCost == minimumTourCost) tour = insertionTsp.Tour;
361  else
362  {
363  Opt2Tsp<Node> opt2Tsp = new Opt2Tsp<Node>(cost, insertionTsp.Tour, insertionTsp.TourCost);
364  opt2Tsp.Run();
365  if (opt2Tsp.TourCost == minimumTourCost) tour = opt2Tsp.Tour;
366  }
367 
368  // convert the tour (node sequence) into a path (arc sequence connecting two nodes)
369  if (tour == null) Cycle = null;
370  else
371  {
372  var cycle = new Path(Graph);
373  if (tour.Any())
374  {
375  Node prev = Node.Invalid;
376  foreach (var n in tour)
377  {
378  if (prev == Node.Invalid) cycle.Begin(n);
379  else cycle.AddLast(Graph.Arcs(prev, n, ArcFilter.Forward).First());
380 
381  prev = n;
382  }
383  cycle.AddLast(Graph.Arcs(prev, tour.First(), ArcFilter.Forward).First());
384  } // if tour is not empty
385  Cycle = cycle;
386  }
387  }
388  }
389 }