Until recently at work, we’ve been using the excellent QuickGraph library for finding optimal haul routes across pits. This week, I replaced it with my own creation, SpryGraph, written from the ground up for performance. (SpryGraph is not a replacement for QuickGraph, due to only having a tiny subset of the features)
Path finding is a much-researched topic in computer science. For a good overview see this paper [pdf].
Some brief performance comparisons:
| Vertex Count | Degree | Algorithm | Query Time – SpryGraph | Query Time – QuickGraph |
|---|---|---|---|---|
| 200,000 | 2 | Dijkstra | 145.9ms | 903.3ms |
| 1,000,000 | 4 | A* | 513.5ms | 9625.7ms |
How to use it:
First define your graph, vertex and edge classes:
using System;
using System.Collections.Generic;
using Alastri.SpryGraph;
namespace MyApp
{
public class MyGraph : IMinimalPathFindingGraph<MyVertex,MyEdge>
{
public IReadOnlyList<MyEdge> GetOutEdges(MyVertex vertex)
{
return vertex.OutEdges;
}
}
public class MyEdge : ICostedEdge<MyVertex>
{
public MyEdge(MyVertex source, MyVertex target)
{
Source = source;
Target = target;
}
public MyVertex Source { get; private set; }
public MyVertex Target { get; private set; }
public double GetCost()
{
return MyVertex.Distance(Source, Target);
}
}
public class MyVertex : IHeuristicVertex<MyVertex>
{
public MyVertex(double x, double y, double z, IReadOnlyList<MyEdge> outEdges)
{
X = x;
Y = y;
OutEdges = outEdges;
Z = z;
}
public double X { get; private set; }
public double Y { get; private set; }
public double Z { get; private set; }
public IReadOnlyList<MyEdge> OutEdges { get; private set; }
public static double Distance(MyVertex a, MyVertex b)
{
double dx = a.X - b.X;
double dy = a.Y - b.Y;
double dz = a.Z - b.Z;
return Math.Sqrt(dx * dx + dy * dy + dz * dz);
}
public double Heuristic(MyVertex destination)
{
return Distance(this, destination);
}
}
}
Second, create some code to do path finding:
using System;
using Alastri.SpryGraph;
namespace MyApp
{
class Program
{
static void Main()
{
//create our graph
MyGraph myGraph = new MyGraph();
//create some vertices
MyVertex v1 = new MyVertex(1,2,3);
MyVertex v2 = new MyVertex(4,5,6);
//add an edge from v1 -> v2
v1.OutEdges = new[] {new MyEdge(v1, v2)};
//read the graph into an efficient data structure
GraphReader<MyVertex, MyEdge> graphReader = new GraphReader<MyVertex, MyEdge>(myGraph);
//get a re-usable path finder, starting at v1
IPathFinder<MyVertex,MyEdge> pathFinder = graphReader.GetAStarPathFinder(v1);
//find path to v2
MyEdge[] path;
if (pathFinder.TryGetPath(v2, out path))
{
foreach (var item in path)
{
Console.WriteLine("{0},{1},{2} -> {3},{4},{5}", v1.X, v1.Y, v1.Z, v2.X, v2.Y, v2.Z);
}
}
}
}
}
If successful, it should print:
1,2,3 -> 4,5,6
The full source is available here.

Leave a comment