Merge branch 'costFun'

This commit is contained in:
Jsor
2014-02-06 03:26:49 -07:00
3 changed files with 73 additions and 57 deletions

View File

@@ -120,3 +120,6 @@ type DStarGraph interface {
Move(target Node) Move(target Node)
ChangedEdges() (newCostFunc func(Node, Node) float64, changedEdges []Edge) ChangedEdges() (newCostFunc func(Node, Node) float64, changedEdges []Edge)
} }
// A function that returns the cost from one node to another
type CostFun func(Node, Node) float64

View File

@@ -30,13 +30,14 @@ import (
// To run Uniform Cost Search, run A* with the NullHeuristic // To run Uniform Cost Search, run A* with the NullHeuristic
// //
// To run Breadth First Search, run A* with both the NullHeuristic and UniformCost (or any cost function that returns a uniform positive value) // To run Breadth First Search, run A* with both the NullHeuristic and UniformCost (or any cost function that returns a uniform positive value)
func AStar(start, goal gr.Node, graph gr.Graph, Cost, HeuristicCost func(gr.Node, gr.Node) float64) (path []gr.Node, cost float64, nodesExpanded int) { func AStar(start, goal gr.Node, graph gr.Graph, cost, heuristicCost gr.CostFun) (path []gr.Node, pathCost float64, nodesExpanded int) {
successors, _, _, _, _, _, Cost, HeuristicCost := setupFuncs(graph, Cost, HeuristicCost) sf := setupFuncs(graph, cost, heuristicCost)
successors, cost, heuristicCost := sf.successors, sf.cost, sf.heuristicCost
closedSet := make(map[int]internalNode) closedSet := make(map[int]internalNode)
openSet := &aStarPriorityQueue{nodes: make([]internalNode, 0), indexList: make(map[int]int)} openSet := &aStarPriorityQueue{nodes: make([]internalNode, 0), indexList: make(map[int]int)}
heap.Init(openSet) heap.Init(openSet)
node := internalNode{start, 0, HeuristicCost(start, goal)} node := internalNode{start, 0, heuristicCost(start, goal)}
heap.Push(openSet, node) heap.Push(openSet, node)
predecessor := make(map[int]gr.Node) predecessor := make(map[int]gr.Node)
@@ -56,15 +57,15 @@ func AStar(start, goal gr.Node, graph gr.Graph, Cost, HeuristicCost func(gr.Node
continue continue
} }
g := curr.gscore + Cost(curr.Node, neighbor) g := curr.gscore + cost(curr.Node, neighbor)
if existing, exists := openSet.Find(neighbor.ID()); !exists { if existing, exists := openSet.Find(neighbor.ID()); !exists {
predecessor[neighbor.ID()] = curr predecessor[neighbor.ID()] = curr
node = internalNode{neighbor, g, g + HeuristicCost(neighbor, goal)} node = internalNode{neighbor, g, g + heuristicCost(neighbor, goal)}
heap.Push(openSet, node) heap.Push(openSet, node)
} else if g < existing.gscore { } else if g < existing.gscore {
predecessor[neighbor.ID()] = curr predecessor[neighbor.ID()] = curr
openSet.Fix(neighbor.ID(), g, g+HeuristicCost(neighbor, goal)) openSet.Fix(neighbor.ID(), g, g+heuristicCost(neighbor, goal))
} }
} }
} }
@@ -89,8 +90,10 @@ func BreadthFirstSearch(start, goal gr.Node, graph gr.Graph) ([]gr.Node, int) {
// Like A*, Dijkstra's Algorithm likely won't run correctly with negative edge weights -- use Bellman-Ford for that instead // Like A*, Dijkstra's Algorithm likely won't run correctly with negative edge weights -- use Bellman-Ford for that instead
// //
// Dijkstra's algorithm usually only returns a cost map, however, since the data is available this version will also reconstruct the path to every node // Dijkstra's algorithm usually only returns a cost map, however, since the data is available this version will also reconstruct the path to every node
func Dijkstra(source gr.Node, graph gr.Graph, Cost func(gr.Node, gr.Node) float64) (paths map[int][]gr.Node, costs map[int]float64) { func Dijkstra(source gr.Node, graph gr.Graph, cost gr.CostFun) (paths map[int][]gr.Node, costs map[int]float64) {
successors, _, _, _, _, _, Cost, _ := setupFuncs(graph, Cost, nil)
sf := setupFuncs(graph, cost, nil)
successors, cost := sf.successors, sf.cost
nodes := graph.NodeList() nodes := graph.NodeList()
openSet := &aStarPriorityQueue{nodes: make([]internalNode, 0), indexList: make(map[int]int)} openSet := &aStarPriorityQueue{nodes: make([]internalNode, 0), indexList: make(map[int]int)}
@@ -111,7 +114,7 @@ func Dijkstra(source gr.Node, graph gr.Graph, Cost func(gr.Node, gr.Node) float6
closedSet.Add(node.ID()) closedSet.Add(node.ID())
for _, neighbor := range successors(node) { for _, neighbor := range successors(node) {
tmpCost := costs[node.ID()] + Cost(node, neighbor) tmpCost := costs[node.ID()] + cost(node, neighbor)
if cost, ok := costs[neighbor.ID()]; !ok { if cost, ok := costs[neighbor.ID()]; !ok {
costs[neighbor.ID()] = tmpCost costs[neighbor.ID()] = tmpCost
predecessor[neighbor.ID()] = node predecessor[neighbor.ID()] = node
@@ -140,8 +143,9 @@ func Dijkstra(source gr.Node, graph gr.Graph, Cost func(gr.Node, gr.Node) float6
// //
// Like Dijkstra's, along with the costs this implementation will also construct all the paths for you. In addition, it has a third return value which will be true if the algorithm was aborted // Like Dijkstra's, along with the costs this implementation will also construct all the paths for you. In addition, it has a third return value which will be true if the algorithm was aborted
// due to the presence of a negative edge weight cycle. // due to the presence of a negative edge weight cycle.
func BellmanFord(source gr.Node, graph gr.Graph, Cost func(gr.Node, gr.Node) float64) (paths map[int][]gr.Node, costs map[int]float64, err error) { func BellmanFord(source gr.Node, graph gr.Graph, cost gr.CostFun) (paths map[int][]gr.Node, costs map[int]float64, err error) {
successors, _, _, _, _, _, Cost, _ := setupFuncs(graph, Cost, nil) sf := setupFuncs(graph, cost, nil)
successors, cost := sf.successors, sf.cost
predecessor := make(map[int]gr.Node) predecessor := make(map[int]gr.Node)
costs = make(map[int]float64) costs = make(map[int]float64)
@@ -155,7 +159,7 @@ func BellmanFord(source gr.Node, graph gr.Graph, Cost func(gr.Node, gr.Node) flo
nodeIDMap[node.ID()] = node nodeIDMap[node.ID()] = node
succs := successors(node) succs := successors(node)
for _, succ := range succs { for _, succ := range succs {
weight := Cost(node, succ) weight := cost(node, succ)
nodeIDMap[succ.ID()] = succ nodeIDMap[succ.ID()] = succ
if dist := costs[node.ID()] + weight; dist < costs[succ.ID()] { if dist := costs[node.ID()] + weight; dist < costs[succ.ID()] {
@@ -169,7 +173,7 @@ func BellmanFord(source gr.Node, graph gr.Graph, Cost func(gr.Node, gr.Node) flo
for _, node := range nodes { for _, node := range nodes {
for _, succ := range successors(node) { for _, succ := range successors(node) {
weight := Cost(node, succ) weight := cost(node, succ)
if costs[node.ID()]+weight < costs[succ.ID()] { if costs[node.ID()]+weight < costs[succ.ID()] {
return nil, nil, errors.New("Negative edge cycle detected") return nil, nil, errors.New("Negative edge cycle detected")
} }
@@ -195,8 +199,10 @@ func BellmanFord(source gr.Node, graph gr.Graph, Cost func(gr.Node, gr.Node) flo
// //
// Its return values are, in order: a map from the source node, to the destination node, to the path between them; a map from the source node, to the destination node, to the cost of the path between them; // Its return values are, in order: a map from the source node, to the destination node, to the path between them; a map from the source node, to the destination node, to the cost of the path between them;
// and a bool that is true if Bellman-Ford detected a negative edge weight cycle -- thus causing it (and this algorithm) to abort (if aborted is true, both maps will be nil). // and a bool that is true if Bellman-Ford detected a negative edge weight cycle -- thus causing it (and this algorithm) to abort (if aborted is true, both maps will be nil).
func Johnson(graph gr.Graph, Cost func(gr.Node, gr.Node) float64) (nodePaths map[int]map[int][]gr.Node, nodeCosts map[int]map[int]float64, err error) { func Johnson(graph gr.Graph, cost gr.CostFun) (nodePaths map[int]map[int][]gr.Node, nodeCosts map[int]map[int]float64, err error) {
successors, _, _, _, _, _, Cost, _ := setupFuncs(graph, Cost, nil) sf := setupFuncs(graph, cost, nil)
successors, cost := sf.successors, sf.cost
/* Copy graph into a mutable one since it has to be altered for this algorithm */ /* Copy graph into a mutable one since it has to be altered for this algorithm */
dummyGraph := concrete.NewGonumGraph(true) dummyGraph := concrete.NewGonumGraph(true)
for _, node := range graph.NodeList() { for _, node := range graph.NodeList() {
@@ -204,12 +210,12 @@ func Johnson(graph gr.Graph, Cost func(gr.Node, gr.Node) float64) (nodePaths map
if !dummyGraph.NodeExists(node) { if !dummyGraph.NodeExists(node) {
dummyGraph.AddNode(node, neighbors) dummyGraph.AddNode(node, neighbors)
for _, neighbor := range neighbors { for _, neighbor := range neighbors {
dummyGraph.SetEdgeCost(concrete.GonumEdge{node, neighbor}, Cost(node, neighbor)) dummyGraph.SetEdgeCost(concrete.GonumEdge{node, neighbor}, cost(node, neighbor))
} }
} else { } else {
for _, neighbor := range neighbors { for _, neighbor := range neighbors {
dummyGraph.AddEdge(concrete.GonumEdge{node, neighbor}) dummyGraph.AddEdge(concrete.GonumEdge{node, neighbor})
dummyGraph.SetEdgeCost(concrete.GonumEdge{node, neighbor}, Cost(node, neighbor)) dummyGraph.SetEdgeCost(concrete.GonumEdge{node, neighbor}, cost(node, neighbor))
} }
} }
} }
@@ -229,7 +235,7 @@ func Johnson(graph gr.Graph, Cost func(gr.Node, gr.Node) float64) (nodePaths map
/* Step 3: reweight the graph and remove the dummy node */ /* Step 3: reweight the graph and remove the dummy node */
for _, node := range graph.NodeList() { for _, node := range graph.NodeList() {
for _, succ := range successors(node) { for _, succ := range successors(node) {
dummyGraph.SetEdgeCost(concrete.GonumEdge{node, succ}, Cost(node, succ)+costs[node.ID()]-costs[succ.ID()]) dummyGraph.SetEdgeCost(concrete.GonumEdge{node, succ}, cost(node, succ)+costs[node.ID()]-costs[succ.ID()])
} }
} }
@@ -249,7 +255,9 @@ func Johnson(graph gr.Graph, Cost func(gr.Node, gr.Node) float64) (nodePaths map
// Expands the first node it sees trying to find the destination. Depth First Search is *not* guaranteed to find the shortest path, // Expands the first node it sees trying to find the destination. Depth First Search is *not* guaranteed to find the shortest path,
// however, if a path exists DFS is guaranteed to find it (provided you don't find a way to implement a Graph with an infinite depth) // however, if a path exists DFS is guaranteed to find it (provided you don't find a way to implement a Graph with an infinite depth)
func DepthFirstSearch(start, goal gr.Node, graph gr.Graph) []gr.Node { func DepthFirstSearch(start, goal gr.Node, graph gr.Graph) []gr.Node {
successors, _, _, _, _, _, _, _ := setupFuncs(graph, nil, nil) sf := setupFuncs(graph, nil, nil)
successors := sf.successors
closedSet := set.NewSet() closedSet := set.NewSet()
openSet := xifo.GonumStack([]interface{}{start}) openSet := xifo.GonumStack([]interface{}{start})
predecessor := make(map[int]gr.Node) predecessor := make(map[int]gr.Node)
@@ -299,7 +307,8 @@ func CopyGraph(dst gr.MutableGraph, src gr.Graph) {
dst.EmptyGraph() dst.EmptyGraph()
dst.SetDirected(false) dst.SetDirected(false)
successors, _, _, _, _, _, cost, _ := setupFuncs(src, nil, nil) sf := setupFuncs(src, nil, nil)
successors, cost := sf.successors, sf.cost
for _, node := range src.NodeList() { for _, node := range src.NodeList() {
succs := successors(node) succs := successors(node)
@@ -335,7 +344,7 @@ func Tarjan(graph gr.Graph) (sccs [][]gr.Node) {
lowlinks := make(map[int]int, len(nodes)) lowlinks := make(map[int]int, len(nodes))
indices := make(map[int]int, len(nodes)) indices := make(map[int]int, len(nodes))
successors, _, _, _, _, _, _, _ := setupFuncs(graph, nil, nil) successors := setupFuncs(graph, nil, nil).successors
var strongconnect func(gr.Node) []gr.Node var strongconnect func(gr.Node) []gr.Node
@@ -384,7 +393,8 @@ func Tarjan(graph gr.Graph) (sccs [][]gr.Node) {
// //
// Special case: a nil or zero length path is considered valid (true), a path of length 1 (only one node) is the trivial case, but only if the node listed in path exists. // Special case: a nil or zero length path is considered valid (true), a path of length 1 (only one node) is the trivial case, but only if the node listed in path exists.
func IsPath(path []gr.Node, graph gr.Graph) bool { func IsPath(path []gr.Node, graph gr.Graph) bool {
_, _, _, isSuccessor, _, _, _, _ := setupFuncs(graph, nil, nil) isSuccessor := setupFuncs(graph, nil, nil).isSuccessor
if path == nil || len(path) == 0 { if path == nil || len(path) == 0 {
return true return true
} else if len(path) == 1 { } else if len(path) == 1 {
@@ -405,14 +415,9 @@ func IsPath(path []gr.Node, graph gr.Graph) bool {
// Generates a minimum spanning tree with sets. // Generates a minimum spanning tree with sets.
// //
// As with other algorithms that use Cost, the order of precedence is Argument > Interface > UniformCost // As with other algorithms that use Cost, the order of precedence is Argument > Interface > UniformCost
func Prim(dst gr.MutableGraph, graph gr.EdgeListGraph, Cost func(gr.Node, gr.Node) float64) { func Prim(dst gr.MutableGraph, graph gr.EdgeListGraph, cost gr.CostFun) {
if Cost == nil { cost = setupFuncs(graph, cost, nil).cost
if cgraph, ok := graph.(gr.Coster); ok {
Cost = cgraph.Cost
} else {
Cost = UniformCost
}
}
dst.EmptyGraph() dst.EmptyGraph()
dst.SetDirected(false) dst.SetDirected(false)
@@ -433,9 +438,9 @@ func Prim(dst gr.MutableGraph, graph gr.EdgeListGraph, Cost func(gr.Node, gr.Nod
edgeWeights := make(edgeSorter, 0) edgeWeights := make(edgeSorter, 0)
for _, edge := range edgeList { for _, edge := range edgeList {
if dst.NodeExists(edge.Head()) && remainingNodes.Contains(edge.Tail().ID()) { if dst.NodeExists(edge.Head()) && remainingNodes.Contains(edge.Tail().ID()) {
edgeWeights = append(edgeWeights, WeightedEdge{Edge: edge, Weight: Cost(edge.Head(), edge.Tail())}) edgeWeights = append(edgeWeights, WeightedEdge{Edge: edge, Weight: cost(edge.Head(), edge.Tail())})
} else if dst.NodeExists(edge.Tail()) && remainingNodes.Contains(edge.Head().ID()) { } else if dst.NodeExists(edge.Tail()) && remainingNodes.Contains(edge.Head().ID()) {
edgeWeights = append(edgeWeights, WeightedEdge{Edge: edge, Weight: Cost(edge.Tail(), edge.Head())}) edgeWeights = append(edgeWeights, WeightedEdge{Edge: edge, Weight: cost(edge.Tail(), edge.Head())})
} }
} }
@@ -459,7 +464,8 @@ func Prim(dst gr.MutableGraph, graph gr.EdgeListGraph, Cost func(gr.Node, gr.Nod
// //
// As with other algorithms with Cost, the precedence goes Argument > Interface > UniformCost // As with other algorithms with Cost, the precedence goes Argument > Interface > UniformCost
func Kruskal(dst gr.MutableGraph, graph gr.EdgeListGraph, cost func(gr.Node, gr.Node) float64) { func Kruskal(dst gr.MutableGraph, graph gr.EdgeListGraph, cost func(gr.Node, gr.Node) float64) {
_, _, _, _, _, _, cost, _ = setupFuncs(graph, cost, nil) cost = setupFuncs(graph, cost, nil).cost
dst.EmptyGraph() dst.EmptyGraph()
dst.SetDirected(false) dst.SetDirected(false)
@@ -506,7 +512,7 @@ func Dominators(start gr.Node, graph gr.Graph) map[int]*set.Set {
allNodes.Add(node.ID()) allNodes.Add(node.ID())
} }
_, predecessors, _, _, _, _, _, _ := setupFuncs(graph, nil, nil) predecessors := setupFuncs(graph, nil, nil).predecessors
for _, node := range nlist { for _, node := range nlist {
dominators[node.ID()] = set.NewSet() dominators[node.ID()] = set.NewSet()
@@ -550,7 +556,8 @@ func Dominators(start gr.Node, graph gr.Graph) map[int]*set.Set {
// //
// This returns all possible post-dominators for all nodes, it does not prune for strict postdominators, immediate postdominators etc // This returns all possible post-dominators for all nodes, it does not prune for strict postdominators, immediate postdominators etc
func PostDominators(end gr.Node, graph gr.Graph) map[int]*set.Set { func PostDominators(end gr.Node, graph gr.Graph) map[int]*set.Set {
successors, _, _, _, _, _, _, _ := setupFuncs(graph, nil, nil) successors := setupFuncs(graph, nil, nil).successors
allNodes := set.NewSet() allNodes := set.NewSet()
nlist := graph.NodeList() nlist := graph.NodeList()
dominators := make(map[int]*set.Set, len(nlist)) dominators := make(map[int]*set.Set, len(nlist))

View File

@@ -6,51 +6,57 @@ import (
gr "github.com/gonum/graph" gr "github.com/gonum/graph"
) )
type searchFuncs struct {
successors, predecessors, neighbors func(gr.Node) []gr.Node
isSuccessor, isPredecessor, isNeighbor func(gr.Node, gr.Node) bool
cost, heuristicCost gr.CostFun
}
// Sets up the cost functions and successor functions so I don't have to do a type switch every time. // 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" // 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. // should be negligible.
func setupFuncs(graph gr.Graph, cost, heuristicCost func(gr.Node, gr.Node) float64) (successorsFunc, predecessorsFunc, neighborsFunc func(gr.Node) []gr.Node, isSuccessorFunc, isPredecessorFunc, func setupFuncs(graph gr.Graph, cost, heuristicCost gr.CostFun) searchFuncs {
isNeighborFunc func(gr.Node, gr.Node) bool,
costFunc, heuristicCostFunc func(gr.Node, gr.Node) float64) { sf := searchFuncs{}
switch g := graph.(type) { switch g := graph.(type) {
case gr.DirectedGraph: case gr.DirectedGraph:
successorsFunc = g.Successors sf.successors = g.Successors
predecessorsFunc = g.Predecessors sf.predecessors = g.Predecessors
neighborsFunc = g.Neighbors sf.neighbors = g.Neighbors
isSuccessorFunc = g.IsSuccessor sf.isSuccessor = g.IsSuccessor
isPredecessorFunc = g.IsPredecessor sf.isPredecessor = g.IsPredecessor
isNeighborFunc = g.IsNeighbor sf.isNeighbor = g.IsNeighbor
default: default:
successorsFunc = g.Neighbors sf.successors = g.Neighbors
predecessorsFunc = g.Neighbors sf.predecessors = g.Neighbors
neighborsFunc = g.Neighbors sf.neighbors = g.Neighbors
isSuccessorFunc = g.IsNeighbor sf.isSuccessor = g.IsNeighbor
isPredecessorFunc = g.IsNeighbor sf.isPredecessor = g.IsNeighbor
isNeighborFunc = g.IsNeighbor sf.isNeighbor = g.IsNeighbor
} }
if heuristicCost != nil { if heuristicCost != nil {
heuristicCostFunc = heuristicCost sf.heuristicCost = heuristicCost
} else { } else {
if g, ok := graph.(gr.HeuristicCoster); ok { if g, ok := graph.(gr.HeuristicCoster); ok {
heuristicCostFunc = g.HeuristicCost sf.heuristicCost = g.HeuristicCost
} else { } else {
heuristicCostFunc = NullHeuristic sf.heuristicCost = NullHeuristic
} }
} }
if cost != nil { if cost != nil {
costFunc = cost sf.cost = cost
} else { } else {
if g, ok := graph.(gr.Coster); ok { if g, ok := graph.(gr.Coster); ok {
costFunc = g.Cost sf.cost = g.Cost
} else { } else {
costFunc = UniformCost sf.cost = UniformCost
} }
} }
return return sf
} }
/* Purely internal data structures and functions (mostly for sorting) */ /* Purely internal data structures and functions (mostly for sorting) */