Documentation
¶
Overview ¶
Package dijkstra is a small, dependency-free shortest-path library for Go: Dijkstra and goal-directed A* search over a named directed graph.
It targets spatial pathfinding on small-to-medium maps -- robot / AGV navigation, game grids, road networks -- where vertices carry optional X / Y / Heading coordinates.
- Dijkstra shortest path (DijkstraSearch) and full single-source trees (DijkstraRun).
- A* goal-directed search (AStarSearch / AStarSearchFunc) with a built-in Euclidean heuristic or a caller-supplied HeuristicFunc.
- One-way edges, dynamic vertex blocking, and runtime graph editing (add / remove vertices and edges) -- each search reflects the current graph.
- A binary-heap priority queue and O(1) name lookups; zero third-party dependencies (Go 1.18+).
Exported types use an St prefix (StGraph, StVertex, StEdge, StPath) and methods return a (bool, T) success flag, kept consistent with the author's other Go code.
All exported StGraph methods are safe for concurrent use; internally a sync.RWMutex serialises mutations and searches while allowing parallel reads of an idle graph.
Index ¶
- func DefaultVertexLine(v StVertex) string
- func Distance(x1, y1, x2, y2 float64) float64
- func DistanceCM(x1, y1, x2, y2 float64) float64
- func EuclideanHeuristic(curX, curY, goalX, goalY float64) float64
- func PathString(path []StPath) string
- type HeuristicFunc
- type StEdge
- type StGraph
- func (g *StGraph) AStarSearch(fromVertex, toVertex string) (bool, []StPath)
- func (g *StGraph) AStarSearchFunc(fromVertex, toVertex string, h HeuristicFunc) (bool, []StPath)
- func (g *StGraph) Debug(debugEn bool)
- func (g *StGraph) DijkstraInit()
- func (g *StGraph) DijkstraRun(startVertex string) bool
- func (g *StGraph) DijkstraSearch(fromVertex, toVertex string) (bool, []StPath)
- func (g *StGraph) DistanceCMToVertex(x, y float64, name string) (bool, float64)
- func (g *StGraph) EdgeExist(fromVertex, toVertex string) bool
- func (g *StGraph) EdgeGetWeight(fromVertex, toVertex string) float64
- func (g *StGraph) EdgeIsOneWay(fromVertex, toVertex string) bool
- func (g *StGraph) MaskShortEdge(fromVertex, toVertex string) bool
- func (g *StGraph) NearPoint(x, y float64) (string, float64)
- func (g *StGraph) Print()
- func (g *StGraph) PrintDijkstra()
- func (g *StGraph) Show(w io.Writer)
- func (g *StGraph) ShowDijkstra(w io.Writer)
- func (g *StGraph) ShowFunc(w io.Writer, line func(v StVertex) string)
- func (g *StGraph) VertexAdd(name string, x, y, heading float64, edges ...StEdge) bool
- func (g *StGraph) VertexAddEdge(fromVertex, toVertex string, weight float64, isOneWay bool) bool
- func (g *StGraph) VertexBLock(name string) bool
- func (g *StGraph) VertexBLockClear()
- func (g *StGraph) VertexBLockLoad(name []string) bool
- func (g *StGraph) VertexBLockRemove(name string) bool
- func (g *StGraph) VertexFind(name string) int
- func (g *StGraph) VertexGetParent(name string) string
- func (g *StGraph) VertexGetWeight(name string) float64
- func (g *StGraph) VertexIsBLock(name string) bool
- func (g *StGraph) VertexIsExist(name string) bool
- func (g *StGraph) VertexIsMasked(name string) bool
- func (g *StGraph) VertexIsVisited(name string) bool
- func (g *StGraph) VertexLength() int
- func (g *StGraph) VertexRemove(name string) bool
- func (g *StGraph) VertexRemoveEdge(fromVertex, toVertex string) bool
- func (g *StGraph) VertexSetMask(name string)
- func (g *StGraph) VertexSetParent(child, parent string) bool
- func (g *StGraph) VertexSetVisited(name string)
- func (g *StGraph) VertexSetWeight(name string, weight float64)
- func (g *StGraph) VertexSetXY(name string, x, y float64)
- func (g *StGraph) VertexToStPath(name string) (bool, StPath)
- type StPath
- type StPriorityQueue
- func (pq *StPriorityQueue) Clear()
- func (pq *StPriorityQueue) DeQueue() (bool, StPriorityQueueData)
- func (pq *StPriorityQueue) EnQueue(fromVertex string, toVertex string, weight float64)
- func (pq *StPriorityQueue) Head() (bool, StPriorityQueueData)
- func (pq *StPriorityQueue) Len() int
- func (pq *StPriorityQueue) NotEmpty() bool
- func (pq *StPriorityQueue) Print()
- type StPriorityQueueData
- type StStack
- type StVertex
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func DefaultVertexLine ¶ added in v1.7.1
DefaultVertexLine is the formatter Show and Print use when no custom one is supplied. It is exported so a ShowFunc callback can reuse or extend it.
func Distance ¶
Distance returns the Euclidean distance between (x1, y1) and (x2, y2). The unit of the result matches the unit of the inputs.
func DistanceCM ¶
DistanceCM is Distance multiplied by 100, useful when the input coordinates are in metres and the caller wants centimetres.
func EuclideanHeuristic ¶ added in v1.7.0
EuclideanHeuristic is the default heuristic: the straight-line distance from the current vertex to the goal. It is the tightest admissible estimate when edge weights are Euclidean distances.
func PathString ¶ added in v1.7.1
PathString renders a path returned by DijkstraSearch / AStarSearch as "A -> B -> C (cost 6)", so callers don't have to loop over the slice just to print a result. An empty path returns "(no path)".
Types ¶
type HeuristicFunc ¶ added in v1.7.0
HeuristicFunc estimates the remaining cost from a vertex at (curX, curY) to the goal at (goalX, goalY). A* explores vertices in order of f = g + h, where g is the cost already spent to reach the vertex and h is this estimate.
For A* to return a provably shortest path the heuristic must be admissible (never overestimate the true remaining cost). The built-in EuclideanHeuristic is admissible -- and consistent -- whenever edge weights are real spatial distances between the vertices' (X, Y) coordinates, which is the common case for the geometric graphs this package targets (robot / AGV maps, road networks). If your edge weights mean something else (time, a penalty, ...), supply a heuristic in the same units that does not overestimate, or pass one that always returns 0 -- in which case A* degenerates to Dijkstra and is still correct.
type StEdge ¶
type StEdge struct {
ToVertexName string
Weight float64
IsOneWay bool
// contains filtered or unexported fields
}
StEdge is a directed edge from the owning vertex to ToVertexName.
IsOneWay = true means the edge may only be traversed from the owning vertex to ToVertexName during search; the reverse direction will not be promoted to the shortest-path tree.
isShort is set internally by DijkstraRun; it is unexported and not part of the public contract.
type StGraph ¶
type StGraph struct {
// contains filtered or unexported fields
}
StGraph is a directed graph supporting Dijkstra shortest-path search and optional vertex blocking. The zero value is a usable empty graph.
All exported methods are safe for concurrent use. Internally, methods named with a "Locked" suffix assume the caller already holds g.mu and are used by the search internals to avoid re-entrant locking.
func (*StGraph) AStarSearch ¶ added in v1.7.0
AStarSearch finds the shortest path from fromVertex to toVertex using A* with the built-in straight-line (Euclidean) heuristic over the vertices' (X, Y) coordinates.
It returns the same result as DijkstraSearch -- ok plus a slice of StPath nodes ordered source-to-destination with cumulative Cost -- but, because the search is goal-directed, it usually expands far fewer vertices on large spatial graphs. The built-in heuristic assumes edge weights are spatial distances; see HeuristicFunc for when to supply your own via AStarSearchFunc.
Example ¶
ExampleStGraph_AStarSearch finds the same shortest path as DijkstraSearch but goal-directed, using the built-in straight-line heuristic over the vertices' (X, Y) coordinates.
package main
import (
"fmt"
dijkstra "github.com/ultramcu/go-dijkstra"
)
func main() {
var g dijkstra.StGraph
// Vertices on a line at x = 0,1,2,3. Edge weights equal the
// straight-line distance, so the built-in heuristic is admissible.
// A direct S->T hop exists but is slower than going via A, B.
g.VertexAdd("S", 0, 0, 0,
dijkstra.StEdge{ToVertexName: "A", Weight: 1, IsOneWay: true},
dijkstra.StEdge{ToVertexName: "T", Weight: 5, IsOneWay: true},
)
g.VertexAdd("A", 1, 0, 0, dijkstra.StEdge{ToVertexName: "B", Weight: 1, IsOneWay: true})
g.VertexAdd("B", 2, 0, 0, dijkstra.StEdge{ToVertexName: "T", Weight: 1, IsOneWay: true})
g.VertexAdd("T", 3, 0, 0)
ok, path := g.AStarSearch("S", "T")
if !ok {
fmt.Println("no path")
return
}
for _, p := range path {
fmt.Printf("%s (cost %.0f)\n", p.Name, p.Cost)
}
}
Output: S (cost 0) A (cost 1) B (cost 2) T (cost 3)
func (*StGraph) AStarSearchFunc ¶ added in v1.7.0
func (g *StGraph) AStarSearchFunc(fromVertex, toVertex string, h HeuristicFunc) (bool, []StPath)
AStarSearchFunc is AStarSearch with a caller-supplied heuristic. Passing nil uses EuclideanHeuristic. A heuristic that always returns 0 makes A* behave exactly like Dijkstra.
As with DijkstraSearch, this resets all per-vertex search state and holds the graph's mutex for the whole call, so concurrent searches on the same graph serialise (they stay race-free).
func (*StGraph) DijkstraInit ¶
func (g *StGraph) DijkstraInit()
DijkstraInit clears per-vertex Dijkstra state on every vertex (Visited, MaskSearch, Weight, Parent, and the per-edge isShort flag). Called automatically by DijkstraRun; the caller usually does not need to invoke it directly.
func (*StGraph) DijkstraRun ¶
DijkstraRun does the main relaxation pass starting from startVertex. After it returns, every reachable vertex has its Visited flag set, its cumulative Weight populated, its Parent pointer set, and the edges belonging to the shortest-path tree have their isShort flag set.
Returns false only if startVertex doesn't exist in the graph.
func (*StGraph) DijkstraSearch ¶
DijkstraSearch finds the shortest path from fromVertex to toVertex.
On success returns ok = true and a slice of StPath nodes ordered from source to destination, with cumulative Cost on each entry. On failure (either endpoint missing, no path, or the only path crosses a blocked vertex) returns ok = false and a nil slice.
Calling DijkstraSearch implicitly resets all per-vertex Dijkstra state on the graph. The graph's internal mutex is held for the full duration of the call, so concurrent calls on the same graph serialise (they remain safe -- no data races -- but do not run in parallel).
Example ¶
package main
import (
"fmt"
dijkstra "github.com/ultramcu/go-dijkstra"
)
func main() {
var g dijkstra.StGraph
// A directed graph:
//
// A --1--> B --2--> C
// | ^
// +---------5--------+ (direct A->C, but slower)
g.VertexAdd("A", 0, 0, 0,
dijkstra.StEdge{ToVertexName: "B", Weight: 1, IsOneWay: true},
dijkstra.StEdge{ToVertexName: "C", Weight: 5, IsOneWay: true},
)
g.VertexAdd("B", 1, 0, 0,
dijkstra.StEdge{ToVertexName: "C", Weight: 2, IsOneWay: true},
)
g.VertexAdd("C", 2, 0, 0)
ok, path := g.DijkstraSearch("A", "C")
if !ok {
fmt.Println("no path")
return
}
for _, p := range path {
fmt.Printf("%s (cost %.0f)\n", p.Name, p.Cost)
}
}
Output: A (cost 0) B (cost 1) C (cost 3)
func (*StGraph) DistanceCMToVertex ¶
DistanceCMToVertex returns the cm distance from (x, y) to the vertex named `name`. Returns false, 0 if the vertex doesn't exist.
func (*StGraph) EdgeGetWeight ¶
EdgeGetWeight returns the weight of the edge from fromVertex to toVertex, or 0 if no such edge exists.
func (*StGraph) EdgeIsOneWay ¶
EdgeIsOneWay reports whether the edge from fromVertex to toVertex is marked one-way. Returns false if no such edge exists.
func (*StGraph) MaskShortEdge ¶
MaskShortEdge marks the edge from fromVertex to toVertex as part of the shortest-path tree. Returns false if no such edge exists.
func (*StGraph) NearPoint ¶
NearPoint returns the name and distance (in cm) of the vertex whose (X, Y) is closest to (x, y). Returns "" and a very large number on an empty graph.
func (*StGraph) Print ¶
func (g *StGraph) Print()
Print writes the whole graph to stdout. It is shorthand for Show(os.Stdout); use Show to write to any io.Writer, or ShowFunc to customise the per-vertex format.
func (*StGraph) PrintDijkstra ¶
func (g *StGraph) PrintDijkstra()
PrintDijkstra writes every vertex's post-search state and its shortest-path-tree edges to stdout. Shorthand for ShowDijkstra(os.Stdout).
func (*StGraph) Show ¶ added in v1.7.1
Show writes a human-readable dump of the whole graph -- every vertex with its coordinates, cumulative weight, visited flag and outgoing edges -- to w. Unlike Print (which targets stdout) it works with any io.Writer: a file, a bytes.Buffer in a test, or a log. Use ShowFunc to override the per-vertex formatting.
func (*StGraph) ShowDijkstra ¶ added in v1.7.1
ShowDijkstra writes the post-search state of every vertex (cumulative weight, parent, visited) together with only the edges on the shortest-path tree to w. Call it after DijkstraSearch, DijkstraRun or an A* search.
func (*StGraph) ShowFunc ¶ added in v1.7.1
ShowFunc writes the graph to w, formatting each vertex with the caller-supplied line function -- so you can customise the output without re-implementing the iteration or the locking. Passing nil uses DefaultVertexLine (the same format as Show and Print).
line receives a copy of each vertex and should only format it: the graph's read lock is held while it runs, so do not call back into the graph (and do not mutate the vertex's Edges slice) from inside it.
func (*StGraph) VertexAdd ¶
VertexAdd inserts a new vertex with the given name, optional (x, y, heading) metadata, and zero or more outgoing edges.
Returns false if a vertex with this name already exists, or if any edge has a negative weight (Dijkstra requires non-negative weights).
Blocking is applied at search time, not here, so edges may be added in any order relative to VertexBLock / VertexBLockLoad.
func (*StGraph) VertexAddEdge ¶ added in v1.5.1
VertexAddEdge adds an outgoing edge from an existing vertex to toVertex. As with VertexAdd, toVertex need not exist yet. Returns false if fromVertex does not exist, the weight is negative, or an edge from fromVertex to toVertex already exists.
func (*StGraph) VertexBLock ¶
VertexBLock adds an existing vertex to the blocked set. Returns false if name does not exist or is already blocked.
Example ¶
ExampleStGraph_VertexBLock shows dynamic blocking: a vertex blocked at runtime makes the next search reroute around it.
package main
import (
"fmt"
dijkstra "github.com/ultramcu/go-dijkstra"
)
func main() {
var g dijkstra.StGraph
g.VertexAdd("A", 0, 0, 0,
dijkstra.StEdge{ToVertexName: "C", Weight: 1, IsOneWay: true},
dijkstra.StEdge{ToVertexName: "B", Weight: 1, IsOneWay: true},
)
g.VertexAdd("B", 0, 0, 0, dijkstra.StEdge{ToVertexName: "D", Weight: 5, IsOneWay: true})
g.VertexAdd("C", 0, 0, 0, dijkstra.StEdge{ToVertexName: "D", Weight: 1, IsOneWay: true})
g.VertexAdd("D", 0, 0, 0)
show := func(path []dijkstra.StPath) {
out := ""
for i, p := range path {
if i > 0 {
out += " -> "
}
out += p.Name
}
fmt.Println(out)
}
// Cheapest route is via C.
_, path := g.DijkstraSearch("A", "D")
show(path)
// Block C at runtime; the next search reroutes via B.
g.VertexBLock("C")
_, path = g.DijkstraSearch("A", "D")
show(path)
}
Output: A -> C -> D A -> B -> D
func (*StGraph) VertexBLockClear ¶
func (g *StGraph) VertexBLockClear()
VertexBLockClear empties the blocked-vertex set. The change takes effect on the next DijkstraSearch / DijkstraRun.
func (*StGraph) VertexBLockLoad ¶
VertexBLockLoad adds names to the blocked-vertex set. Always returns true.
func (*StGraph) VertexBLockRemove ¶
VertexBLockRemove removes name from the blocked set. Returns true if the set was already empty or the name was removed, false if name was present in neither case.
func (*StGraph) VertexFind ¶
VertexFind returns the slice index of the vertex named `name`, or -1 if not found.
func (*StGraph) VertexGetParent ¶
VertexGetParent returns the parent name set during the last DijkstraRun, or "" if name doesn't exist or has no parent.
func (*StGraph) VertexGetWeight ¶
VertexGetWeight returns the cumulative cost recorded on the vertex during the last DijkstraRun, or 0 if the vertex doesn't exist.
func (*StGraph) VertexIsBLock ¶
VertexIsBLock reports whether name is currently in the blocked set.
func (*StGraph) VertexIsExist ¶
VertexIsExist reports whether a vertex with that name exists.
func (*StGraph) VertexIsMasked ¶
VertexIsMasked reports whether a vertex has been touched by the path-reconstruction stack walk.
func (*StGraph) VertexIsVisited ¶
VertexIsVisited reports whether the vertex has been settled by the last DijkstraRun.
func (*StGraph) VertexLength ¶
VertexLength returns the number of vertices in the graph.
func (*StGraph) VertexRemove ¶ added in v1.5.1
VertexRemove deletes the named vertex from the graph together with every edge (from any vertex) that pointed at it, and drops it from the blocked set. Returns false if the vertex does not exist.
The next DijkstraSearch / DijkstraRun recomputes from scratch, so any stale Dijkstra state left on other vertices is harmless.
func (*StGraph) VertexRemoveEdge ¶ added in v1.5.1
VertexRemoveEdge removes the edge from fromVertex to toVertex. Returns false if fromVertex or the edge does not exist.
func (*StGraph) VertexSetMask ¶
VertexSetMask marks a vertex as touched by the path-reconstruction stack walk.
func (*StGraph) VertexSetParent ¶
VertexSetParent records that `child`'s parent in the shortest-path tree is `parent`. Returns false if child doesn't exist.
func (*StGraph) VertexSetVisited ¶
VertexSetVisited marks a vertex as settled.
func (*StGraph) VertexSetWeight ¶
VertexSetWeight overwrites the cumulative cost on a vertex.
func (*StGraph) VertexSetXY ¶
VertexSetXY updates the X and Y coordinates of an existing vertex. No-op if the vertex doesn't exist.
type StPath ¶
StPath is one node in a returned shortest path. Cost is the cumulative weight from the source up to and including this node.
type StPriorityQueue ¶
type StPriorityQueue struct {
// contains filtered or unexported fields
}
StPriorityQueue is an ascending-by-weight priority queue of StPriorityQueueData implemented as a binary min-heap, so EnQueue and DeQueue are O(log n). Entries with equal weight are dequeued in insertion order (FIFO within an equivalence class).
func (*StPriorityQueue) DeQueue ¶
func (pq *StPriorityQueue) DeQueue() (bool, StPriorityQueueData)
DeQueue removes and returns the lowest-weight entry in O(log n). Returns (false, zero-value) if the queue is empty.
func (*StPriorityQueue) EnQueue ¶
func (pq *StPriorityQueue) EnQueue(fromVertex string, toVertex string, weight float64)
EnQueue inserts a new entry in O(log n).
func (*StPriorityQueue) Head ¶
func (pq *StPriorityQueue) Head() (bool, StPriorityQueueData)
Head returns the lowest-weight entry without removing it. Returns (false, zero-value) if the queue is empty.
func (*StPriorityQueue) Len ¶
func (pq *StPriorityQueue) Len() int
Len returns the number of entries currently in the queue.
func (*StPriorityQueue) NotEmpty ¶
func (pq *StPriorityQueue) NotEmpty() bool
NotEmpty is the negation of empty; convenient as a loop predicate.
func (*StPriorityQueue) Print ¶
func (pq *StPriorityQueue) Print()
Print writes the queue's contents to stdout in heap-array order.
type StPriorityQueueData ¶
type StPriorityQueueData struct {
// contains filtered or unexported fields
}
StPriorityQueueData is one entry in the priority queue used by the Dijkstra relaxation pass: an edge (fromVertex -> toVertex) together with the cumulative cost to reach toVertex via that edge.
type StStack ¶
type StStack struct {
// contains filtered or unexported fields
}
StStack is a simple LIFO stack of strings. Exported for users who want to do their own graph traversal on top of the StGraph types.
func (*StStack) Pop ¶
Pop removes and returns the top of the stack. Returns "" if the stack is empty.
type StVertex ¶
type StVertex struct {
Name string
Weight float64
Visited bool
X float64
Y float64
Heading float64
Edges []StEdge
MaskSearch bool
Parent string
}
StVertex is one node in the graph.
X / Y / Heading are optional metadata for spatial graphs. They are returned verbatim in StPath but are not consulted by the algorithm itself, so non-geometric users can leave them at zero.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
example
|
|
|
astar
command
Command astar-example demonstrates AStarSearch on a spatial grid and shows that A*, guided by the straight-line heuristic, finds the same optimal path as Dijkstra while expanding far fewer vertices.
|
Command astar-example demonstrates AStarSearch on a spatial grid and shows that A*, guided by the straight-line heuristic, finds the same optimal path as Dijkstra while expanding far fewer vertices. |
|
compare
command
Command compare is a runnable performance comparison of Dijkstra vs A* over grids of several sizes and two query shapes, printing the average time per search and the number of vertices each expands.
|
Command compare is a runnable performance comparison of Dijkstra vs A* over grids of several sizes and two query shapes, printing the average time per search and the number of vertices each expands. |
|
dijkstra
command
Command dijkstra-example demonstrates DijkstraSearch on a small weighted directed graph, plus dynamic vertex blocking.
|
Command dijkstra-example demonstrates DijkstraSearch on a small weighted directed graph, plus dynamic vertex blocking. |