mirror of
https://github.com/gonum/gonum.git
synced 2025-10-19 05:24:52 +08:00
213 lines
5.1 KiB
Go
213 lines
5.1 KiB
Go
// Copyright ©2014 The gonum Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package search
|
|
|
|
import (
|
|
"container/heap"
|
|
|
|
"github.com/gonum/graph"
|
|
"github.com/gonum/graph/concrete"
|
|
)
|
|
|
|
type searchFuncs struct {
|
|
successors, predecessors, neighbors func(graph.Node) []graph.Node
|
|
isSuccessor, isPredecessor, isNeighbor func(graph.Node, graph.Node) bool
|
|
cost graph.CostFunc
|
|
heuristicCost graph.HeuristicCostFunc
|
|
edgeTo, edgeBetween func(graph.Node, graph.Node) graph.Edge
|
|
}
|
|
|
|
func genIsSuccessor(gr graph.DirectedGraph) func(graph.Node, graph.Node) bool {
|
|
return func(node, succ graph.Node) bool {
|
|
return gr.EdgeTo(node, succ) != nil
|
|
}
|
|
}
|
|
|
|
func genIsPredecessor(gr graph.DirectedGraph) func(graph.Node, graph.Node) bool {
|
|
return func(node, succ graph.Node) bool {
|
|
return gr.EdgeTo(succ, node) != nil
|
|
}
|
|
}
|
|
|
|
func genIsNeighbor(gr graph.Graph) func(graph.Node, graph.Node) bool {
|
|
return func(node, succ graph.Node) bool {
|
|
return gr.EdgeBetween(succ, node) != nil
|
|
}
|
|
}
|
|
|
|
// Sets up the cost functions and successor functions so I don't have to do a type switch every
|
|
// time. This almost always does more work than is necessary, but since it's only executed once
|
|
// per function, and graph functions are rather costly, the "extra work" should be negligible.
|
|
func setupFuncs(gr graph.Graph, cost graph.CostFunc, heuristicCost graph.HeuristicCostFunc) searchFuncs {
|
|
|
|
sf := searchFuncs{}
|
|
|
|
switch g := gr.(type) {
|
|
case graph.DirectedGraph:
|
|
sf.successors = g.Successors
|
|
sf.predecessors = g.Predecessors
|
|
sf.neighbors = g.Neighbors
|
|
sf.isSuccessor = genIsSuccessor(g)
|
|
sf.isPredecessor = genIsPredecessor(g)
|
|
sf.isNeighbor = genIsNeighbor(g)
|
|
sf.edgeBetween = g.EdgeBetween
|
|
sf.edgeTo = g.EdgeTo
|
|
default:
|
|
sf.successors = g.Neighbors
|
|
sf.predecessors = g.Neighbors
|
|
sf.neighbors = g.Neighbors
|
|
isNeighbor := genIsNeighbor(g)
|
|
sf.isSuccessor = isNeighbor
|
|
sf.isPredecessor = isNeighbor
|
|
sf.isNeighbor = isNeighbor
|
|
sf.edgeBetween = g.EdgeBetween
|
|
sf.edgeTo = g.EdgeBetween
|
|
}
|
|
|
|
if heuristicCost != nil {
|
|
sf.heuristicCost = heuristicCost
|
|
} else {
|
|
if g, ok := gr.(graph.HeuristicCoster); ok {
|
|
sf.heuristicCost = g.HeuristicCost
|
|
} else {
|
|
sf.heuristicCost = NullHeuristic
|
|
}
|
|
}
|
|
|
|
if cost != nil {
|
|
sf.cost = cost
|
|
} else {
|
|
if g, ok := gr.(graph.Coster); ok {
|
|
sf.cost = g.Cost
|
|
} else {
|
|
sf.cost = UniformCost
|
|
}
|
|
}
|
|
|
|
return sf
|
|
}
|
|
|
|
/** Sorts a list of edges by weight, agnostic to repeated edges as well as direction **/
|
|
|
|
type edgeSorter []concrete.WeightedEdge
|
|
|
|
func (el edgeSorter) Len() int {
|
|
return len(el)
|
|
}
|
|
|
|
func (el edgeSorter) Less(i, j int) bool {
|
|
return el[i].Cost < el[j].Cost
|
|
}
|
|
|
|
func (el edgeSorter) Swap(i, j int) {
|
|
el[i], el[j] = el[j], el[i]
|
|
}
|
|
|
|
/** Keeps track of a node's scores so they can be used in a priority queue for A* **/
|
|
|
|
type internalNode struct {
|
|
graph.Node
|
|
gscore, fscore float64
|
|
}
|
|
|
|
/* A* stuff */
|
|
type aStarPriorityQueue struct {
|
|
indexList map[int]int
|
|
nodes []internalNode
|
|
}
|
|
|
|
func (pq *aStarPriorityQueue) Less(i, j int) bool {
|
|
// As the heap documentation says, a priority queue is listed if the actual values
|
|
// are treated as if they were negative
|
|
return pq.nodes[i].fscore < pq.nodes[j].fscore
|
|
}
|
|
|
|
func (pq *aStarPriorityQueue) Swap(i, j int) {
|
|
pq.indexList[pq.nodes[i].ID()] = j
|
|
pq.indexList[pq.nodes[j].ID()] = i
|
|
|
|
pq.nodes[i], pq.nodes[j] = pq.nodes[j], pq.nodes[i]
|
|
}
|
|
|
|
func (pq *aStarPriorityQueue) Len() int {
|
|
return len(pq.nodes)
|
|
}
|
|
|
|
func (pq *aStarPriorityQueue) Push(x interface{}) {
|
|
node := x.(internalNode)
|
|
pq.nodes = append(pq.nodes, node)
|
|
pq.indexList[node.ID()] = len(pq.nodes) - 1
|
|
}
|
|
|
|
func (pq *aStarPriorityQueue) Pop() interface{} {
|
|
x := pq.nodes[len(pq.nodes)-1]
|
|
pq.nodes = pq.nodes[:len(pq.nodes)-1]
|
|
delete(pq.indexList, x.ID())
|
|
|
|
return x
|
|
}
|
|
|
|
func (pq *aStarPriorityQueue) Fix(id int, newGScore, newFScore float64) {
|
|
if i, ok := pq.indexList[id]; ok {
|
|
pq.nodes[i].gscore = newGScore
|
|
pq.nodes[i].fscore = newFScore
|
|
heap.Fix(pq, i)
|
|
}
|
|
}
|
|
|
|
func (pq *aStarPriorityQueue) Find(id int) (internalNode, bool) {
|
|
loc, ok := pq.indexList[id]
|
|
if ok {
|
|
return pq.nodes[loc], true
|
|
} else {
|
|
return internalNode{}, false
|
|
}
|
|
|
|
}
|
|
|
|
func (pq *aStarPriorityQueue) Exists(id int) bool {
|
|
_, ok := pq.indexList[id]
|
|
return ok
|
|
}
|
|
|
|
type denseNodeSorter []graph.Node
|
|
|
|
func (dns denseNodeSorter) Less(i, j int) bool {
|
|
return dns[i].ID() < dns[j].ID()
|
|
}
|
|
|
|
func (dns denseNodeSorter) Swap(i, j int) {
|
|
dns[i], dns[j] = dns[j], dns[i]
|
|
}
|
|
|
|
func (dns denseNodeSorter) Len() int {
|
|
return len(dns)
|
|
}
|
|
|
|
// General utility funcs
|
|
|
|
// Rebuilds a path backwards from the goal.
|
|
func rebuildPath(predecessors map[int]graph.Node, goal graph.Node) []graph.Node {
|
|
if n, ok := goal.(internalNode); ok {
|
|
goal = n.Node
|
|
}
|
|
path := []graph.Node{goal}
|
|
curr := goal
|
|
for prev, ok := predecessors[curr.ID()]; ok; prev, ok = predecessors[curr.ID()] {
|
|
if n, ok := prev.(internalNode); ok {
|
|
prev = n.Node
|
|
}
|
|
path = append(path, prev)
|
|
curr = prev
|
|
}
|
|
|
|
// Reverse the path since it was built backwards
|
|
for i, j := 0, len(path)-1; i < j; i, j = i+1, j-1 {
|
|
path[i], path[j] = path[j], path[i]
|
|
}
|
|
|
|
return path
|
|
}
|