Skip to content
This repository has been archived by the owner on Apr 26, 2019. It is now read-only.

Commit

Permalink
{network,search}: remove weight parameter
Browse files Browse the repository at this point in the history
Fixes #83.
  • Loading branch information
kortschak committed Jun 29, 2015
1 parent 20a8231 commit 7411c8c
Show file tree
Hide file tree
Showing 12 changed files with 77 additions and 99 deletions.
2 changes: 1 addition & 1 deletion network/betweenness_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ func TestBetweennessWeighted(t *testing.T) {
}
}

p, ok := search.FloydWarshall(g, nil)
p, ok := search.FloydWarshall(g)
if !ok {
t.Errorf("unexpected negative cycle in test %d", i)
continue
Expand Down
4 changes: 2 additions & 2 deletions network/distance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ func TestDistanceCentralityUndirected(t *testing.T) {
g.AddUndirectedEdge(concrete.Edge{F: concrete.Node(u), T: concrete.Node(v)}, 1)
}
}
p, ok := search.FloydWarshall(g, nil)
p, ok := search.FloydWarshall(g)
if !ok {
t.Errorf("unexpected negative cycle in test %d", i)
continue
Expand Down Expand Up @@ -343,7 +343,7 @@ func TestDistanceCentralityDirected(t *testing.T) {
g.AddDirectedEdge(concrete.Edge{F: concrete.Node(u), T: concrete.Node(v)}, 1)
}
}
p, ok := search.FloydWarshall(g, nil)
p, ok := search.FloydWarshall(g)
if !ok {
t.Errorf("unexpected negative cycle in test %d", i)
continue
Expand Down
17 changes: 8 additions & 9 deletions search/bellman_ford_moore.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,19 @@ package search
import "github.com/gonum/graph"

// BellmanFordFrom returns a shortest-path tree for a shortest path from u to all nodes in
// the graph g, or false indicating that a negative cycle exists in the graph. If weight
// is nil and the graph does not implement graph.Coster, UniformCost is used.
// the graph g, or false indicating that a negative cycle exists in the graph. If the graph
// does not implement graph.Coster, UniformCost is used.
//
// The time complexity of BellmanFordFrom is O(|V|.|E|).
func BellmanFordFrom(u graph.Node, g graph.Graph, weight graph.CostFunc) (path Shortest, ok bool) {
func BellmanFordFrom(u graph.Node, g graph.Graph) (path Shortest, ok bool) {
if !g.Has(u) {
return Shortest{from: u}, true
}
if weight == nil {
if g, ok := g.(graph.Coster); ok {
weight = g.Cost
} else {
weight = UniformCost
}
var weight graph.CostFunc
if g, ok := g.(graph.Coster); ok {
weight = g.Cost
} else {
weight = UniformCost
}

nodes := g.Nodes()
Expand Down
2 changes: 1 addition & 1 deletion search/bellman_ford_moore_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ func TestBellmanFordFrom(t *testing.T) {
}
}

pt, ok := search.BellmanFordFrom(test.query.From(), g.(graph.Graph), nil)
pt, ok := search.BellmanFordFrom(test.query.From(), g.(graph.Graph))
if test.hasNegativeCycle {
if ok {
t.Errorf("%q: expected negative cycle", test.name)
Expand Down
36 changes: 17 additions & 19 deletions search/dijkstra.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,19 @@ import (
)

// DijkstraFrom returns a shortest-path tree for a shortest path from u to all nodes in
// the graph g. If weight is nil and the graph does not implement graph.Coster, UniformCost
// is used. DijkstraFrom will panic if g has a u-reachable negative edge weight.
// the graph g. If the graph does not implement graph.Coster, UniformCost is used.
// DijkstraFrom will panic if g has a u-reachable negative edge weight.
//
// The time complexity of DijkstrFrom is O(|E|+|V|.log|V|).
func DijkstraFrom(u graph.Node, g graph.Graph, weight graph.CostFunc) Shortest {
func DijkstraFrom(u graph.Node, g graph.Graph) Shortest {
if !g.Has(u) {
return Shortest{from: u}
}
if weight == nil {
if g, ok := g.(graph.Coster); ok {
weight = g.Cost
} else {
weight = UniformCost
}
var weight graph.CostFunc
if g, ok := g.(graph.Coster); ok {
weight = g.Cost
} else {
weight = UniformCost
}

nodes := g.Nodes()
Expand Down Expand Up @@ -60,27 +59,26 @@ func DijkstraFrom(u graph.Node, g graph.Graph, weight graph.CostFunc) Shortest {
}

// DijkstraAllPaths returns a shortest-path tree for shortest paths in the graph g.
// If weight is nil and the graph does not implement graph.Coster, UniformCost is used.
// If the graph does not implement graph.Coster, UniformCost is used.
// DijkstraAllPaths will panic if g has a negative edge weight.
//
// The time complexity of DijkstrAllPaths is O(|V|.|E|+|V|^2.log|V|).
func DijkstraAllPaths(g graph.Graph, weight graph.CostFunc) (paths AllShortest) {
func DijkstraAllPaths(g graph.Graph) (paths AllShortest) {
paths = newAllShortest(g.Nodes(), false)
dijkstraAllPaths(g, weight, paths)
dijkstraAllPaths(g, paths)
return paths
}

// dijkstraAllPaths is the all-paths implementation of Dijkstra. It is shared
// between DijkstraAllPaths and JohnsonAllPaths to avoid repeated allocation
// of the nodes slice and the indexOf map. It returns nothing, but stores the
// result of the work in the paths parameter which is a reference type.
func dijkstraAllPaths(g graph.Graph, weight graph.CostFunc, paths AllShortest) {
if weight == nil {
if g, ok := g.(graph.Coster); ok {
weight = g.Cost
} else {
weight = UniformCost
}
func dijkstraAllPaths(g graph.Graph, paths AllShortest) {
var weight graph.CostFunc
if g, ok := g.(graph.Coster); ok {
weight = g.Cost
} else {
weight = UniformCost
}

var Q priorityQueue
Expand Down
4 changes: 2 additions & 2 deletions search/dijkstra_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func TestDijkstraFrom(t *testing.T) {
defer func() {
panicked = recover() != nil
}()
pt = search.DijkstraFrom(test.query.From(), g.(graph.Graph), nil)
pt = search.DijkstraFrom(test.query.From(), g.(graph.Graph))
}()
if panicked || test.negative {
if !test.negative {
Expand Down Expand Up @@ -111,7 +111,7 @@ func TestDijkstraAllPaths(t *testing.T) {
defer func() {
panicked = recover() != nil
}()
pt = search.DijkstraAllPaths(g.(graph.Graph), nil)
pt = search.DijkstraAllPaths(g.(graph.Graph))
}()
if panicked || test.negative {
if !test.negative {
Expand Down
17 changes: 8 additions & 9 deletions search/floydwarshall.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,16 @@ package search
import "github.com/gonum/graph"

// FloydWarshall returns a shortest-path tree for the graph g or false indicating
// that a negative cycle exists in the graph. If weight is nil and the graph does not
// implement graph.Coster, UniformCost is used.
// that a negative cycle exists in the graph. If the graph does not implement
// graph.Coster, UniformCost is used.
//
// The time complexity of FloydWarshall is O(|V|^3).
func FloydWarshall(g graph.Graph, weight graph.CostFunc) (paths AllShortest, ok bool) {
if weight == nil {
if g, ok := g.(graph.Coster); ok {
weight = g.Cost
} else {
weight = UniformCost
}
func FloydWarshall(g graph.Graph) (paths AllShortest, ok bool) {
var weight graph.CostFunc
if g, ok := g.(graph.Coster); ok {
weight = g.Cost
} else {
weight = UniformCost
}

nodes := g.Nodes()
Expand Down
2 changes: 1 addition & 1 deletion search/floydwarshall_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ func TestFloydWarshall(t *testing.T) {
}
}

pt, ok := search.FloydWarshall(g.(graph.Graph), nil)
pt, ok := search.FloydWarshall(g.(graph.Graph))
if test.hasNegativeCycle {
if ok {
t.Errorf("%q: expected negative cycle", test.name)
Expand Down
55 changes: 20 additions & 35 deletions search/graph_search.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,25 +32,17 @@ import (
// max(NonConsistentHeuristicCost(neighbor,goal), NonConsistentHeuristicCost(self,goal) -
// Cost(self,neighbor)). If there are multiple neighbors, take the max of all of them.
//
// Cost and HeuristicCost take precedence for evaluating cost/heuristic distance. If one is not
// present (i.e. nil) the function will check the graph's interface for the respective interface:
// Coster for Cost and HeuristicCoster for HeuristicCost. If the correct one is present, it will
// use the graph's function for evaluation.
//
// Finally, if neither the argument nor the interface is present, the function will assume
// UniformCost for Cost and NullHeuristic for HeuristicCost.
//
// To run Uniform Cost Search, run A* with the NullHeuristic.
// If heuristice is nil, A* will use the graph's HeuristicCost method if present, otherwise
// falling back to 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.)
func AStar(start, goal graph.Node, g graph.Graph, weight graph.CostFunc, heuristic graph.HeuristicCostFunc) (path []graph.Node, pathCost float64, nodesExpanded int) {
if weight == nil {
if g, ok := g.(graph.Coster); ok {
weight = g.Cost
} else {
weight = UniformCost
}
func AStar(start, goal graph.Node, g graph.Graph, heuristic graph.HeuristicCostFunc) (path []graph.Node, pathCost float64, nodesExpanded int) {
var weight graph.CostFunc
if g, ok := g.(graph.Coster); ok {
weight = g.Cost
} else {
weight = UniformCost
}
if heuristic == nil {
if g, ok := g.(graph.HeuristicCoster); ok {
Expand Down Expand Up @@ -335,17 +327,13 @@ puts the resulting minimum spanning tree in the dst graph */

// Generates a minimum spanning tree with sets.
//
// As with other algorithms that use Cost, the order of precedence is
// Argument > Interface > UniformCost.
//
// The destination must be empty (or at least disjoint with the node IDs of the input)
func Prim(dst graph.MutableGraph, g graph.EdgeListGraph, weight graph.CostFunc) {
if weight == nil {
if g, ok := g.(graph.Coster); ok {
weight = g.Cost
} else {
weight = UniformCost
}
func Prim(dst graph.MutableGraph, g graph.EdgeListGraph) {
var weight graph.CostFunc
if g, ok := g.(graph.Coster); ok {
weight = g.Cost
} else {
weight = UniformCost
}

nlist := g.Nodes()
Expand Down Expand Up @@ -382,16 +370,13 @@ func Prim(dst graph.MutableGraph, g graph.EdgeListGraph, weight graph.CostFunc)

// Generates a minimum spanning tree for a graph using discrete.DisjointSet.
//
// As with other algorithms with Cost, the precedence goes Argument > Interface > UniformCost.
//
// The destination must be empty (or at least disjoint with the node IDs of the input)
func Kruskal(dst graph.MutableGraph, g graph.EdgeListGraph, weight graph.CostFunc) {
if weight == nil {
if g, ok := g.(graph.Coster); ok {
weight = g.Cost
} else {
weight = UniformCost
}
func Kruskal(dst graph.MutableGraph, g graph.EdgeListGraph) {
var weight graph.CostFunc
if g, ok := g.(graph.Coster); ok {
weight = g.Cost
} else {
weight = UniformCost
}

edgeList := g.Edges()
Expand Down
19 changes: 8 additions & 11 deletions search/johnson_apsp.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,19 @@ import (
)

// JohnsonAllPaths returns a shortest-path tree for shortest paths in the graph g.
// If weight is nil and the graph does not implement graph.Coster, UniformCost is used.
// If the graph does not implement graph.Coster, UniformCost is used.
//
// The time complexity of JohnsonAllPaths is O(|V|.|E|+|V|^2.log|V|).
func JohnsonAllPaths(g graph.Graph, weight graph.CostFunc) (paths AllShortest, ok bool) {
func JohnsonAllPaths(g graph.Graph) (paths AllShortest, ok bool) {
jg := johnsonWeightAdjuster{
g: g,
from: g.From,
weight: weight,
edgeTo: g.Edge,
}
if jg.weight == nil {
if g, ok := g.(graph.Coster); ok {
jg.weight = g.Cost
} else {
jg.weight = UniformCost
}
if g, ok := g.(graph.Coster); ok {
jg.weight = g.Cost
} else {
jg.weight = UniformCost
}

paths = newAllShortest(g.Nodes(), false)
Expand All @@ -45,13 +42,13 @@ func JohnsonAllPaths(g graph.Graph, weight graph.CostFunc) (paths AllShortest, o
}

jg.bellmanFord = true
jg.adjustBy, ok = BellmanFordFrom(johnsonGraphNode(jg.q), jg, nil)
jg.adjustBy, ok = BellmanFordFrom(johnsonGraphNode(jg.q), jg)
if !ok {
return paths, false
}

jg.bellmanFord = false
dijkstraAllPaths(jg, nil, paths)
dijkstraAllPaths(jg, paths)

for i, u := range paths.nodes {
hu := jg.adjustBy.WeightTo(u)
Expand Down
2 changes: 1 addition & 1 deletion search/johnson_apsp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ func TestJohnsonAllPaths(t *testing.T) {
}
}

pt, ok := search.JohnsonAllPaths(g.(graph.Graph), nil)
pt, ok := search.JohnsonAllPaths(g.(graph.Graph))
if test.hasNegativeCycle {
if ok {
t.Errorf("%q: expected negative cycle", test.name)
Expand Down
16 changes: 8 additions & 8 deletions search/search_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ func TestSimpleAStar(t *testing.T) {
t.Fatalf("Couldn't generate tilegraph: %v", err)
}

path, cost, _ := search.AStar(concrete.Node(1), concrete.Node(14), tg, nil, nil)
path, cost, _ := search.AStar(concrete.Node(1), concrete.Node(14), tg, nil)
if math.Abs(cost-4) > 1e-5 {
t.Errorf("A* reports incorrect cost for simple tilegraph search")
}
Expand All @@ -51,14 +51,14 @@ func TestSimpleAStar(t *testing.T) {
func TestBiggerAStar(t *testing.T) {
tg := internal.NewTileGraph(3, 3, true)

path, cost, _ := search.AStar(concrete.Node(0), concrete.Node(8), tg, nil, nil)
path, cost, _ := search.AStar(concrete.Node(0), concrete.Node(8), tg, nil)

if math.Abs(cost-4) > 1e-5 || !search.IsPath(path, tg) {
t.Error("Non-optimal or impossible path found for 3x3 grid")
}

tg = internal.NewTileGraph(1000, 1000, true)
path, cost, _ = search.AStar(concrete.Node(0), concrete.Node(999*1000+999), tg, nil, nil)
path, cost, _ = search.AStar(concrete.Node(0), concrete.Node(999*1000+999), tg, nil)
if !search.IsPath(path, tg) || cost != 1998 {
t.Error("Non-optimal or impossible path found for 100x100 grid; cost:", cost, "path:\n"+tg.PathString(path))
}
Expand All @@ -79,7 +79,7 @@ func TestObstructedAStar(t *testing.T) {
tg.SetPassability(4, 9, false)

rows, cols := tg.Dimensions()
path, cost1, expanded := search.AStar(concrete.Node(5), tg.CoordsToNode(rows-1, cols-1), tg, nil, nil)
path, cost1, expanded := search.AStar(concrete.Node(5), tg.CoordsToNode(rows-1, cols-1), tg, nil)

if !search.IsPath(path, tg) {
t.Error("Path doesn't exist in obstructed graph")
Expand All @@ -93,7 +93,7 @@ func TestObstructedAStar(t *testing.T) {
return math.Abs(float64(r1)-float64(r2)) + math.Abs(float64(c1)-float64(c2))
}

path, cost2, expanded2 := search.AStar(concrete.Node(5), tg.CoordsToNode(rows-1, cols-1), tg, nil, ManhattanHeuristic)
path, cost2, expanded2 := search.AStar(concrete.Node(5), tg.CoordsToNode(rows-1, cols-1), tg, ManhattanHeuristic)
if !search.IsPath(path, tg) {
t.Error("Path doesn't exist when using heuristic on obstructed graph")
}
Expand All @@ -119,7 +119,7 @@ func TestNoPathAStar(t *testing.T) {
tg.SetPassability(2, 4, false)

rows, _ := tg.Dimensions()
path, _, _ := search.AStar(tg.CoordsToNode(0, 2), tg.CoordsToNode(rows-1, 2), tg, nil, nil)
path, _, _ := search.AStar(tg.CoordsToNode(0, 2), tg.CoordsToNode(rows-1, 2), tg, nil)

if len(path) > 0 { // Note that a nil slice will return len of 0, this won't panic
t.Error("A* finds path where none exists")
Expand All @@ -133,10 +133,10 @@ func TestSmallAStar(t *testing.T) {
t.Fatalf("non-monotonic heuristic. edge: %v goal: %v", edge, goal)
}

ps := search.DijkstraAllPaths(g, nil)
ps := search.DijkstraAllPaths(g)
for _, start := range g.Nodes() {
for _, goal := range g.Nodes() {
gotPath, gotWeight, _ := search.AStar(start, goal, g, nil, heur)
gotPath, gotWeight, _ := search.AStar(start, goal, g, heur)
wantPath, wantWeight, _ := ps.Between(start, goal)
if gotWeight != wantWeight {
t.Errorf("unexpected A* path weight from %v to %v result: got:%s want:%s",
Expand Down

0 comments on commit 7411c8c

Please sign in to comment.