mirror of
https://github.com/gonum/gonum.git
synced 2025-10-18 05:00:43 +08:00
Merge pull request #8 from gonum/edge
All goals met, maybe reconsider MutableGraph interface further in the future into Directed/Undirected forms.
This commit is contained in:
@@ -54,7 +54,7 @@ func (dg *DenseGraph) Degree(node graph.Node) int {
|
||||
func (dg *DenseGraph) NodeList() []graph.Node {
|
||||
nodes := make([]graph.Node, dg.numNodes)
|
||||
for i := 0; i < dg.numNodes; i++ {
|
||||
nodes[i] = GonumNode(i)
|
||||
nodes[i] = Node(i)
|
||||
}
|
||||
|
||||
return nodes
|
||||
@@ -65,7 +65,7 @@ func (dg *DenseGraph) DirectedEdgeList() []graph.Edge {
|
||||
for i := 0; i < dg.numNodes; i++ {
|
||||
for j := 0; j < dg.numNodes; j++ {
|
||||
if dg.adjacencyMatrix[i*dg.numNodes+j] != math.Inf(1) {
|
||||
edges = append(edges, GonumEdge{GonumNode(i), GonumNode(j)})
|
||||
edges = append(edges, Edge{Node(i), Node(j)})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -78,70 +78,71 @@ func (dg *DenseGraph) Neighbors(node graph.Node) []graph.Node {
|
||||
for i := 0; i < dg.numNodes; i++ {
|
||||
if dg.adjacencyMatrix[i*dg.numNodes+node.ID()] != math.Inf(1) ||
|
||||
dg.adjacencyMatrix[node.ID()*dg.numNodes+i] != math.Inf(1) {
|
||||
neighbors = append(neighbors, GonumNode(i))
|
||||
neighbors = append(neighbors, Node(i))
|
||||
}
|
||||
}
|
||||
|
||||
return neighbors
|
||||
}
|
||||
|
||||
func (dg *DenseGraph) IsNeighbor(node, neighbor graph.Node) bool {
|
||||
return dg.adjacencyMatrix[neighbor.ID()*dg.numNodes+node.ID()] != math.Inf(1) ||
|
||||
dg.adjacencyMatrix[node.ID()*dg.numNodes+neighbor.ID()] != math.Inf(1)
|
||||
func (dg *DenseGraph) EdgeBetween(node, neighbor graph.Node) graph.Edge {
|
||||
if dg.adjacencyMatrix[neighbor.ID()*dg.numNodes+node.ID()] != math.Inf(1) ||
|
||||
dg.adjacencyMatrix[node.ID()*dg.numNodes+neighbor.ID()] != math.Inf(1) {
|
||||
return Edge{node, neighbor}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dg *DenseGraph) Successors(node graph.Node) []graph.Node {
|
||||
neighbors := make([]graph.Node, 0)
|
||||
for i := 0; i < dg.numNodes; i++ {
|
||||
if dg.adjacencyMatrix[node.ID()*dg.numNodes+i] != math.Inf(1) {
|
||||
neighbors = append(neighbors, GonumNode(i))
|
||||
neighbors = append(neighbors, Node(i))
|
||||
}
|
||||
}
|
||||
|
||||
return neighbors
|
||||
}
|
||||
|
||||
func (dg *DenseGraph) IsSuccessor(node, succ graph.Node) bool {
|
||||
return dg.adjacencyMatrix[node.ID()*dg.numNodes+succ.ID()] != math.Inf(1)
|
||||
func (dg *DenseGraph) EdgeTo(node, succ graph.Node) graph.Edge {
|
||||
if dg.adjacencyMatrix[node.ID()*dg.numNodes+succ.ID()] != math.Inf(1) {
|
||||
return Edge{node, succ}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dg *DenseGraph) Predecessors(node graph.Node) []graph.Node {
|
||||
neighbors := make([]graph.Node, 0)
|
||||
for i := 0; i < dg.numNodes; i++ {
|
||||
if dg.adjacencyMatrix[i*dg.numNodes+node.ID()] != math.Inf(1) {
|
||||
neighbors = append(neighbors, GonumNode(i))
|
||||
neighbors = append(neighbors, Node(i))
|
||||
}
|
||||
}
|
||||
|
||||
return neighbors
|
||||
}
|
||||
|
||||
func (dg *DenseGraph) IsPredecessor(node, pred graph.Node) bool {
|
||||
return dg.adjacencyMatrix[pred.ID()*dg.numNodes+node.ID()] != math.Inf(1)
|
||||
}
|
||||
|
||||
// DenseGraph is naturally dense, we don't need to do anything
|
||||
func (dg *DenseGraph) Crunch() {
|
||||
}
|
||||
|
||||
func (dg *DenseGraph) Cost(node, succ graph.Node) float64 {
|
||||
return dg.adjacencyMatrix[node.ID()*dg.numNodes+succ.ID()]
|
||||
func (dg *DenseGraph) Cost(e graph.Edge) float64 {
|
||||
return dg.adjacencyMatrix[e.Head().ID()*dg.numNodes+e.Tail().ID()]
|
||||
}
|
||||
|
||||
// Sets the cost of the edge between node and succ. If the cost is +Inf, it will remove the edge,
|
||||
// Sets the cost of an edge. If the cost is +Inf, it will remove the edge,
|
||||
// if directed is true, it will only remove the edge one way. If it's false it will change the cost
|
||||
// of the edge from succ to node as well.
|
||||
func (dg *DenseGraph) SetEdgeCost(node, succ graph.Node, cost float64, directed bool) {
|
||||
dg.adjacencyMatrix[node.ID()*dg.numNodes+succ.ID()] = cost
|
||||
func (dg *DenseGraph) SetEdgeCost(e graph.Edge, cost float64, directed bool) {
|
||||
dg.adjacencyMatrix[e.Head().ID()*dg.numNodes+e.Tail().ID()] = cost
|
||||
if !directed {
|
||||
dg.adjacencyMatrix[succ.ID()*dg.numNodes+node.ID()] = cost
|
||||
dg.adjacencyMatrix[e.Tail().ID()*dg.numNodes+e.Head().ID()] = cost
|
||||
}
|
||||
}
|
||||
|
||||
// More or less equivalent to SetEdgeCost(node, succ, math.Inf(1), directed)
|
||||
func (dg *DenseGraph) RemoveEdge(node, succ graph.Node, directed bool) {
|
||||
dg.adjacencyMatrix[node.ID()*dg.numNodes+succ.ID()] = math.Inf(1)
|
||||
if !directed {
|
||||
dg.adjacencyMatrix[succ.ID()*dg.numNodes+node.ID()] = math.Inf(1)
|
||||
}
|
||||
// Equivalent to SetEdgeCost(edge, math.Inf(1), directed)
|
||||
func (dg *DenseGraph) RemoveEdge(e graph.Edge, directed bool) {
|
||||
dg.SetEdgeCost(e, math.Inf(1), directed)
|
||||
}
|
||||
|
@@ -18,17 +18,17 @@ func TestBasicDenseImpassable(t *testing.T) {
|
||||
}
|
||||
|
||||
for i := 0; i < 5; i++ {
|
||||
if !dg.NodeExists(concrete.GonumNode(i)) {
|
||||
if !dg.NodeExists(concrete.Node(i)) {
|
||||
t.Errorf("Node that should exist doesn't: %d", i)
|
||||
}
|
||||
|
||||
if degree := dg.Degree(concrete.GonumNode(i)); degree != 0 {
|
||||
if degree := dg.Degree(concrete.Node(i)); degree != 0 {
|
||||
t.Errorf("Node in impassable graph has a neighbor. Node: %d Degree: %d", i, degree)
|
||||
}
|
||||
}
|
||||
|
||||
for i := 5; i < 10; i++ {
|
||||
if dg.NodeExists(concrete.GonumNode(i)) {
|
||||
if dg.NodeExists(concrete.Node(i)) {
|
||||
t.Errorf("Node exists that shouldn't: %d")
|
||||
}
|
||||
}
|
||||
@@ -41,17 +41,17 @@ func TestBasicDensePassable(t *testing.T) {
|
||||
}
|
||||
|
||||
for i := 0; i < 5; i++ {
|
||||
if !dg.NodeExists(concrete.GonumNode(i)) {
|
||||
if !dg.NodeExists(concrete.Node(i)) {
|
||||
t.Errorf("Node that should exist doesn't: %d", i)
|
||||
}
|
||||
|
||||
if degree := dg.Degree(concrete.GonumNode(i)); degree != 10 {
|
||||
if degree := dg.Degree(concrete.Node(i)); degree != 10 {
|
||||
t.Errorf("Node in impassable graph has a neighbor. Node: %d Degree: %d", i, degree)
|
||||
}
|
||||
}
|
||||
|
||||
for i := 5; i < 10; i++ {
|
||||
if dg.NodeExists(concrete.GonumNode(i)) {
|
||||
if dg.NodeExists(concrete.Node(i)) {
|
||||
t.Errorf("Node exists that shouldn't: %d")
|
||||
}
|
||||
}
|
||||
@@ -59,81 +59,81 @@ func TestBasicDensePassable(t *testing.T) {
|
||||
|
||||
func TestDenseAddRemove(t *testing.T) {
|
||||
dg := concrete.NewDenseGraph(10, false)
|
||||
dg.SetEdgeCost(concrete.GonumNode(0), concrete.GonumNode(2), 1.0, false)
|
||||
dg.SetEdgeCost(concrete.Edge{concrete.Node(0), concrete.Node(2)}, 1.0, false)
|
||||
|
||||
if neighbors := dg.Neighbors(concrete.GonumNode(0)); len(neighbors) != 1 || neighbors[0].ID() != 2 ||
|
||||
!dg.IsNeighbor(concrete.GonumNode(0), concrete.GonumNode(2)) {
|
||||
if neighbors := dg.Neighbors(concrete.Node(0)); len(neighbors) != 1 || neighbors[0].ID() != 2 ||
|
||||
dg.EdgeBetween(concrete.Node(0), concrete.Node(2)) == nil {
|
||||
t.Errorf("Couldn't add neighbor")
|
||||
}
|
||||
|
||||
if neighbors := dg.Successors(concrete.GonumNode(0)); len(neighbors) != 1 || neighbors[0].ID() != 2 ||
|
||||
!dg.IsSuccessor(concrete.GonumNode(0), concrete.GonumNode(2)) {
|
||||
if neighbors := dg.Successors(concrete.Node(0)); len(neighbors) != 1 || neighbors[0].ID() != 2 ||
|
||||
dg.EdgeTo(concrete.Node(0), concrete.Node(2)) == nil {
|
||||
t.Errorf("Adding edge didn't create successor")
|
||||
}
|
||||
|
||||
if neighbors := dg.Predecessors(concrete.GonumNode(0)); len(neighbors) != 1 || neighbors[0].ID() != 2 ||
|
||||
!dg.IsPredecessor(concrete.GonumNode(0), concrete.GonumNode(2)) {
|
||||
if neighbors := dg.Predecessors(concrete.Node(0)); len(neighbors) != 1 || neighbors[0].ID() != 2 ||
|
||||
dg.EdgeTo(concrete.Node(2), concrete.Node(0)) == nil {
|
||||
t.Errorf("Adding undirected edge didn't create predecessor")
|
||||
}
|
||||
|
||||
if neighbors := dg.Neighbors(concrete.GonumNode(2)); len(neighbors) != 1 || neighbors[0].ID() != 0 ||
|
||||
!dg.IsNeighbor(concrete.GonumNode(2), concrete.GonumNode(0)) {
|
||||
if neighbors := dg.Neighbors(concrete.Node(2)); len(neighbors) != 1 || neighbors[0].ID() != 0 ||
|
||||
dg.EdgeBetween(concrete.Node(2), concrete.Node(0)) == nil {
|
||||
t.Errorf("Adding an undirected neighbor didn't add it reciprocally")
|
||||
}
|
||||
|
||||
if neighbors := dg.Successors(concrete.GonumNode(2)); len(neighbors) != 1 || neighbors[0].ID() != 0 ||
|
||||
!dg.IsSuccessor(concrete.GonumNode(2), concrete.GonumNode(0)) {
|
||||
if neighbors := dg.Successors(concrete.Node(2)); len(neighbors) != 1 || neighbors[0].ID() != 0 ||
|
||||
dg.EdgeTo(concrete.Node(2), concrete.Node(0)) == nil {
|
||||
t.Errorf("Adding undirected edge didn't create proper successor")
|
||||
}
|
||||
|
||||
if neighbors := dg.Predecessors(concrete.GonumNode(2)); len(neighbors) != 1 || neighbors[0].ID() != 0 ||
|
||||
!dg.IsPredecessor(concrete.GonumNode(2), concrete.GonumNode(0)) {
|
||||
if neighbors := dg.Predecessors(concrete.Node(2)); len(neighbors) != 1 || neighbors[0].ID() != 0 ||
|
||||
dg.EdgeTo(concrete.Node(2), concrete.Node(0)) == nil {
|
||||
t.Errorf("Adding edge didn't create proper predecessor")
|
||||
}
|
||||
|
||||
dg.RemoveEdge(concrete.GonumNode(0), concrete.GonumNode(2), true)
|
||||
dg.RemoveEdge(concrete.Edge{concrete.Node(0), concrete.Node(2)}, true)
|
||||
|
||||
if neighbors := dg.Neighbors(concrete.GonumNode(0)); len(neighbors) != 1 || neighbors[0].ID() != 2 ||
|
||||
!dg.IsNeighbor(concrete.GonumNode(0), concrete.GonumNode(2)) {
|
||||
if neighbors := dg.Neighbors(concrete.Node(0)); len(neighbors) != 1 || neighbors[0].ID() != 2 ||
|
||||
dg.EdgeBetween(concrete.Node(0), concrete.Node(2)) == nil {
|
||||
t.Errorf("Removing a directed edge changed result of neighbors when neighbors is undirected; neighbors: %v", neighbors)
|
||||
}
|
||||
|
||||
if neighbors := dg.Successors(concrete.GonumNode(0)); len(neighbors) != 0 || dg.IsSuccessor(concrete.GonumNode(0), concrete.GonumNode(2)) {
|
||||
if neighbors := dg.Successors(concrete.Node(0)); len(neighbors) != 0 || dg.EdgeTo(concrete.Node(0), concrete.Node(2)) != nil {
|
||||
t.Errorf("Removing edge didn't properly remove successor")
|
||||
}
|
||||
|
||||
if neighbors := dg.Predecessors(concrete.GonumNode(0)); len(neighbors) != 1 || neighbors[0].ID() != 2 ||
|
||||
!dg.IsPredecessor(concrete.GonumNode(0), concrete.GonumNode(2)) {
|
||||
if neighbors := dg.Predecessors(concrete.Node(0)); len(neighbors) != 1 || neighbors[0].ID() != 2 ||
|
||||
dg.EdgeTo(concrete.Node(2), concrete.Node(0)) == nil {
|
||||
t.Errorf("Removing directed edge improperly removed predecessor")
|
||||
}
|
||||
|
||||
if neighbors := dg.Neighbors(concrete.GonumNode(2)); len(neighbors) != 1 || neighbors[0].ID() != 0 ||
|
||||
!dg.IsNeighbor(concrete.GonumNode(2), concrete.GonumNode(0)) {
|
||||
if neighbors := dg.Neighbors(concrete.Node(2)); len(neighbors) != 1 || neighbors[0].ID() != 0 ||
|
||||
dg.EdgeBetween(concrete.Node(2), concrete.Node(0)) == nil {
|
||||
t.Errorf("Removing a directed edge removed reciprocal edge, neighbors: %v", neighbors)
|
||||
}
|
||||
|
||||
if neighbors := dg.Successors(concrete.GonumNode(2)); len(neighbors) != 1 || neighbors[0].ID() != 0 ||
|
||||
!dg.IsSuccessor(concrete.GonumNode(2), concrete.GonumNode(0)) {
|
||||
if neighbors := dg.Successors(concrete.Node(2)); len(neighbors) != 1 || neighbors[0].ID() != 0 ||
|
||||
dg.EdgeTo(concrete.Node(2), concrete.Node(0)) == nil {
|
||||
t.Errorf("Removing edge improperly removed successor")
|
||||
}
|
||||
|
||||
if neighbors := dg.Predecessors(concrete.GonumNode(2)); len(neighbors) != 0 || dg.IsPredecessor(concrete.GonumNode(2), concrete.GonumNode(0)) {
|
||||
if neighbors := dg.Predecessors(concrete.Node(2)); len(neighbors) != 0 || dg.EdgeTo(concrete.Node(0), concrete.Node(2)) != nil {
|
||||
t.Errorf("Removing directed edge wrongly kept predecessor")
|
||||
}
|
||||
|
||||
dg.SetEdgeCost(concrete.GonumNode(0), concrete.GonumNode(2), 2.0, true)
|
||||
dg.SetEdgeCost(concrete.Edge{concrete.Node(0), concrete.Node(2)}, 2.0, true)
|
||||
// I figure we've torture tested Neighbors/Successors/Predecessors at this point
|
||||
// so we'll just use the bool functions now
|
||||
if !dg.IsSuccessor(concrete.GonumNode(0), concrete.GonumNode(2)) {
|
||||
if dg.EdgeTo(concrete.Node(0), concrete.Node(2)) == nil {
|
||||
t.Error("Adding directed edge didn't change successor back")
|
||||
} else if !dg.IsSuccessor(concrete.GonumNode(2), concrete.GonumNode(0)) {
|
||||
} else if dg.EdgeTo(concrete.Node(2), concrete.Node(0)) == nil {
|
||||
t.Error("Adding directed edge strangely removed reverse successor")
|
||||
} else if c1, c2 := dg.Cost(concrete.GonumNode(2), concrete.GonumNode(0)), dg.Cost(concrete.GonumNode(0), concrete.GonumNode(2)); math.Abs(c1-c2) < .000001 {
|
||||
} else if c1, c2 := dg.Cost(concrete.Edge{concrete.Node(2), concrete.Node(0)}), dg.Cost(concrete.Edge{concrete.Node(0), concrete.Node(2)}); math.Abs(c1-c2) < .000001 {
|
||||
t.Error("Adding directed edge affected cost in undirected manner")
|
||||
}
|
||||
|
||||
dg.RemoveEdge(concrete.GonumNode(2), concrete.GonumNode(0), false)
|
||||
if dg.IsSuccessor(concrete.GonumNode(0), concrete.GonumNode(2)) || dg.IsSuccessor(concrete.GonumNode(2), concrete.GonumNode(0)) {
|
||||
dg.RemoveEdge(concrete.Edge{concrete.Node(2), concrete.Node(0)}, false)
|
||||
if dg.EdgeTo(concrete.Node(0), concrete.Node(2)) != nil || dg.EdgeTo(concrete.Node(2), concrete.Node(0)) != nil {
|
||||
t.Error("Removing undirected edge did no work properly")
|
||||
}
|
||||
}
|
||||
@@ -173,7 +173,7 @@ func TestDenseLists(t *testing.T) {
|
||||
t.Errorf("Improper number of edges for passable dense graph")
|
||||
}
|
||||
|
||||
dg.RemoveEdge(concrete.GonumNode(12), concrete.GonumNode(11), true)
|
||||
dg.RemoveEdge(concrete.Edge{concrete.Node(12), concrete.Node(11)}, true)
|
||||
edges = dg.DirectedEdgeList()
|
||||
if len(edges) != (15*15)-1 {
|
||||
t.Errorf("Removing edge didn't affect edge listing properly")
|
||||
|
@@ -4,29 +4,34 @@ import (
|
||||
"math"
|
||||
"sort"
|
||||
|
||||
gr "github.com/gonum/graph"
|
||||
"github.com/gonum/graph"
|
||||
)
|
||||
|
||||
// A simple int alias.
|
||||
type GonumNode int
|
||||
type Node int
|
||||
|
||||
func (node GonumNode) ID() int {
|
||||
func (node Node) ID() int {
|
||||
return int(node)
|
||||
}
|
||||
|
||||
// Just a collection of two nodes
|
||||
type GonumEdge struct {
|
||||
H, T gr.Node
|
||||
type Edge struct {
|
||||
H, T graph.Node
|
||||
}
|
||||
|
||||
func (edge GonumEdge) Head() gr.Node {
|
||||
func (edge Edge) Head() graph.Node {
|
||||
return edge.H
|
||||
}
|
||||
|
||||
func (edge GonumEdge) Tail() gr.Node {
|
||||
func (edge Edge) Tail() graph.Node {
|
||||
return edge.T
|
||||
}
|
||||
|
||||
type WeightedEdge struct {
|
||||
graph.Edge
|
||||
Cost float64
|
||||
}
|
||||
|
||||
// A GonumGraph is a very generalized graph that can handle an arbitrary number of vertices and
|
||||
// edges -- as well as act as either directed or undirected.
|
||||
//
|
||||
@@ -38,35 +43,32 @@ func (edge GonumEdge) Tail() gr.Node {
|
||||
// MutableGraph). For most purposes, creating your own graph is probably better. For instance,
|
||||
// see TileGraph for an example of an immutable 2D grid of tiles that also implements the Graph
|
||||
// interface, but would be more suitable if all you needed was a simple undirected 2D grid.
|
||||
type GonumGraph struct {
|
||||
successors map[int]map[int]float64
|
||||
predecessors map[int]map[int]float64
|
||||
nodeMap map[int]gr.Node
|
||||
directed bool
|
||||
type Graph struct {
|
||||
successors map[int]map[int]WeightedEdge
|
||||
predecessors map[int]map[int]WeightedEdge
|
||||
nodeMap map[int]graph.Node
|
||||
}
|
||||
|
||||
func NewGonumGraph(directed bool) *GonumGraph {
|
||||
return &GonumGraph{
|
||||
successors: make(map[int]map[int]float64),
|
||||
predecessors: make(map[int]map[int]float64),
|
||||
nodeMap: make(map[int]gr.Node),
|
||||
directed: directed,
|
||||
func NewGraph() *Graph {
|
||||
return &Graph{
|
||||
successors: make(map[int]map[int]WeightedEdge),
|
||||
predecessors: make(map[int]map[int]WeightedEdge),
|
||||
nodeMap: make(map[int]graph.Node),
|
||||
}
|
||||
}
|
||||
|
||||
func NewPreAllocatedGonumGraph(directed bool, numVertices int) *GonumGraph {
|
||||
return &GonumGraph{
|
||||
successors: make(map[int]map[int]float64, numVertices),
|
||||
predecessors: make(map[int]map[int]float64, numVertices),
|
||||
nodeMap: make(map[int]gr.Node, numVertices),
|
||||
directed: directed,
|
||||
func NewPreAllocatedGraph(numVertices int) *Graph {
|
||||
return &Graph{
|
||||
successors: make(map[int]map[int]WeightedEdge, numVertices),
|
||||
predecessors: make(map[int]map[int]WeightedEdge, numVertices),
|
||||
nodeMap: make(map[int]graph.Node, numVertices),
|
||||
}
|
||||
}
|
||||
|
||||
/* Mutable Graph implementation */
|
||||
|
||||
func (graph *GonumGraph) NewNode(successors []gr.Node) (node gr.Node) {
|
||||
nodeList := graph.NodeList()
|
||||
func (gr *Graph) NewNode() (node graph.Node) {
|
||||
nodeList := gr.NodeList()
|
||||
ids := make([]int, len(nodeList))
|
||||
for i, node := range nodeList {
|
||||
ids[i] = node.ID()
|
||||
@@ -76,278 +78,211 @@ func (graph *GonumGraph) NewNode(successors []gr.Node) (node gr.Node) {
|
||||
sort.Sort(&nodes)
|
||||
for i, node := range nodes {
|
||||
if i != node {
|
||||
graph.AddNode(GonumNode(i), successors)
|
||||
return GonumNode(i)
|
||||
gr.AddNode(Node(i))
|
||||
return Node(i)
|
||||
}
|
||||
}
|
||||
|
||||
newID := len(nodes)
|
||||
graph.AddNode(GonumNode(newID), successors)
|
||||
return GonumNode(newID)
|
||||
gr.AddNode(Node(newID))
|
||||
return Node(newID)
|
||||
}
|
||||
|
||||
func (graph *GonumGraph) AddNode(node gr.Node, successors []gr.Node) {
|
||||
id := node.ID()
|
||||
if _, ok := graph.successors[id]; ok {
|
||||
func (gr *Graph) AddNode(node graph.Node) {
|
||||
if _, ok := gr.nodeMap[node.ID()]; ok {
|
||||
return
|
||||
}
|
||||
|
||||
graph.nodeMap[id] = node
|
||||
gr.nodeMap[node.ID()] = node
|
||||
gr.successors[node.ID()] = make(map[int]WeightedEdge)
|
||||
gr.predecessors[node.ID()] = make(map[int]WeightedEdge)
|
||||
}
|
||||
|
||||
graph.successors[id] = make(map[int]float64, len(successors))
|
||||
if !graph.directed {
|
||||
graph.predecessors[id] = make(map[int]float64, len(successors))
|
||||
} else {
|
||||
graph.predecessors[id] = make(map[int]float64)
|
||||
}
|
||||
for _, successor := range successors {
|
||||
succ := successor.ID()
|
||||
graph.successors[id][succ] = 1.0
|
||||
func (gr *Graph) AddEdge(e graph.Edge, cost float64, directed bool) {
|
||||
head, tail := e.Head(), e.Tail()
|
||||
gr.AddNode(head)
|
||||
gr.AddNode(tail)
|
||||
|
||||
// Always add the reciprocal node to the graph
|
||||
if _, ok := graph.successors[succ]; !ok {
|
||||
graph.nodeMap[succ] = successor
|
||||
graph.predecessors[succ] = make(map[int]float64)
|
||||
graph.successors[succ] = make(map[int]float64)
|
||||
}
|
||||
|
||||
graph.predecessors[succ][id] = 1.0
|
||||
|
||||
// But only add the reciprocal edge if we're undirected
|
||||
if !graph.directed {
|
||||
graph.successors[succ][id] = 1.0
|
||||
graph.predecessors[id][succ] = 1.0
|
||||
}
|
||||
gr.successors[head.ID()][tail.ID()] = WeightedEdge{Edge: e, Cost: cost}
|
||||
gr.predecessors[tail.ID()][head.ID()] = WeightedEdge{Edge: e, Cost: cost}
|
||||
if !directed {
|
||||
gr.successors[tail.ID()][head.ID()] = WeightedEdge{Edge: e, Cost: cost}
|
||||
gr.predecessors[head.ID()][tail.ID()] = WeightedEdge{Edge: e, Cost: cost}
|
||||
}
|
||||
}
|
||||
|
||||
func (graph *GonumGraph) AddEdge(e gr.Edge) {
|
||||
id := e.Head().ID()
|
||||
successor := e.Tail().ID()
|
||||
if _, ok := graph.successors[id]; !ok {
|
||||
func (gr *Graph) RemoveNode(node graph.Node) {
|
||||
if _, ok := gr.nodeMap[node.ID()]; !ok {
|
||||
return
|
||||
}
|
||||
delete(gr.nodeMap, node.ID())
|
||||
|
||||
if _, ok := graph.successors[successor]; !ok {
|
||||
graph.nodeMap[successor] = e.Tail()
|
||||
graph.successors[successor] = make(map[int]float64)
|
||||
graph.predecessors[successor] = make(map[int]float64)
|
||||
for succ, _ := range gr.successors[node.ID()] {
|
||||
delete(gr.predecessors[succ], node.ID())
|
||||
}
|
||||
delete(gr.successors, node.ID())
|
||||
|
||||
graph.successors[id][successor] = 1.0
|
||||
graph.predecessors[successor][id] = 1.0
|
||||
|
||||
if !graph.directed {
|
||||
graph.successors[successor][id] = 1.0
|
||||
graph.predecessors[id][successor] = 1.0
|
||||
for pred, _ := range gr.predecessors[node.ID()] {
|
||||
delete(gr.successors[pred], node.ID())
|
||||
}
|
||||
}
|
||||
|
||||
func (graph *GonumGraph) SetEdgeCost(e gr.Edge, cost float64) {
|
||||
id := e.Head().ID()
|
||||
successor := e.Tail().ID()
|
||||
// Normally I'd use graph.vertices.Contains(id) as above, but this is equivalent and a bit
|
||||
// easier to read here
|
||||
if _, ok := graph.successors[id]; !ok {
|
||||
return
|
||||
} else if _, ok := graph.successors[id][successor]; !ok {
|
||||
return
|
||||
}
|
||||
graph.successors[id][successor] = cost
|
||||
graph.predecessors[successor][id] = cost
|
||||
|
||||
// By the spec, only the empty graph will be toggled between directed and undirected.
|
||||
// Therefore we can be sure the reciprocal edge exists
|
||||
if !graph.directed {
|
||||
graph.successors[successor][id] = cost
|
||||
graph.predecessors[id][successor] = cost
|
||||
}
|
||||
}
|
||||
|
||||
func (graph *GonumGraph) RemoveNode(node gr.Node) {
|
||||
id := node.ID()
|
||||
if _, ok := graph.successors[id]; !ok {
|
||||
return
|
||||
}
|
||||
delete(graph.nodeMap, id)
|
||||
|
||||
for succ, _ := range graph.successors[id] {
|
||||
delete(graph.predecessors[succ], id)
|
||||
}
|
||||
delete(graph.successors, id)
|
||||
|
||||
for pred, _ := range graph.predecessors[id] {
|
||||
delete(graph.successors[pred], id)
|
||||
}
|
||||
delete(graph.predecessors, id)
|
||||
delete(gr.predecessors, node.ID())
|
||||
|
||||
}
|
||||
|
||||
func (graph *GonumGraph) RemoveEdge(e gr.Edge) {
|
||||
id := e.Head().ID()
|
||||
succ := e.Tail().ID()
|
||||
if _, ok := graph.successors[id]; !ok {
|
||||
func (gr *Graph) RemoveEdge(e graph.Edge, directed bool) {
|
||||
head, tail := e.Head(), e.Tail()
|
||||
if _, ok := gr.nodeMap[head.ID()]; !ok {
|
||||
return
|
||||
} else if _, ok := graph.successors[succ]; !ok {
|
||||
} else if _, ok := gr.nodeMap[tail.ID()]; !ok {
|
||||
return
|
||||
}
|
||||
|
||||
delete(graph.successors[id], succ)
|
||||
delete(graph.predecessors[succ], id)
|
||||
if !graph.directed {
|
||||
delete(graph.predecessors[id], succ)
|
||||
delete(graph.successors[succ], id)
|
||||
delete(gr.successors[head.ID()], tail.ID())
|
||||
delete(gr.predecessors[tail.ID()], head.ID())
|
||||
if !directed {
|
||||
delete(gr.successors[tail.ID()], head.ID())
|
||||
delete(gr.predecessors[head.ID()], tail.ID())
|
||||
}
|
||||
}
|
||||
|
||||
func (graph *GonumGraph) EmptyGraph() {
|
||||
if len(graph.successors) == 0 {
|
||||
return
|
||||
}
|
||||
graph.successors = make(map[int]map[int]float64)
|
||||
graph.predecessors = make(map[int]map[int]float64)
|
||||
graph.nodeMap = make(map[int]gr.Node)
|
||||
}
|
||||
|
||||
func (graph *GonumGraph) SetDirected(directed bool) {
|
||||
if len(graph.successors) > 0 {
|
||||
return
|
||||
}
|
||||
graph.directed = directed
|
||||
func (gr *Graph) EmptyGraph() {
|
||||
gr.successors = make(map[int]map[int]WeightedEdge)
|
||||
gr.predecessors = make(map[int]map[int]WeightedEdge)
|
||||
gr.nodeMap = make(map[int]graph.Node)
|
||||
}
|
||||
|
||||
/* Graph implementation */
|
||||
|
||||
func (graph *GonumGraph) Successors(node gr.Node) []gr.Node {
|
||||
id := node.ID()
|
||||
if _, ok := graph.successors[id]; !ok {
|
||||
func (gr *Graph) Successors(node graph.Node) []graph.Node {
|
||||
if _, ok := gr.successors[node.ID()]; !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
successors := make([]gr.Node, 0, len(graph.successors[id]))
|
||||
for succ, _ := range graph.successors[id] {
|
||||
successors = append(successors, graph.nodeMap[succ])
|
||||
successors := make([]graph.Node, len(gr.successors[node.ID()]))
|
||||
i := 0
|
||||
for succ, _ := range gr.successors[node.ID()] {
|
||||
successors[i] = gr.nodeMap[succ]
|
||||
i++
|
||||
}
|
||||
|
||||
return successors
|
||||
}
|
||||
|
||||
func (graph *GonumGraph) IsSuccessor(node, successor gr.Node) bool {
|
||||
succ := successor.ID()
|
||||
id := node.ID()
|
||||
if _, ok := graph.successors[id]; !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
_, ok := graph.successors[id][succ]
|
||||
|
||||
return ok
|
||||
}
|
||||
|
||||
func (graph *GonumGraph) Predecessors(node gr.Node) []gr.Node {
|
||||
id := node.ID()
|
||||
if _, ok := graph.successors[id]; !ok {
|
||||
func (gr *Graph) EdgeTo(node, succ graph.Node) graph.Edge {
|
||||
if _, ok := gr.nodeMap[node.ID()]; !ok {
|
||||
return nil
|
||||
} else if _, ok := gr.nodeMap[succ.ID()]; !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
predecessors := make([]gr.Node, 0, len(graph.predecessors[id]))
|
||||
for pred, _ := range graph.predecessors[id] {
|
||||
predecessors = append(predecessors, graph.nodeMap[pred])
|
||||
edge, ok := gr.successors[node.ID()][succ.ID()]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return edge
|
||||
}
|
||||
|
||||
func (gr *Graph) Predecessors(node graph.Node) []graph.Node {
|
||||
if _, ok := gr.successors[node.ID()]; !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
predecessors := make([]graph.Node, len(gr.predecessors[node.ID()]))
|
||||
i := 0
|
||||
for succ, _ := range gr.predecessors[node.ID()] {
|
||||
predecessors[i] = gr.nodeMap[succ]
|
||||
i++
|
||||
}
|
||||
|
||||
return predecessors
|
||||
}
|
||||
|
||||
func (graph *GonumGraph) IsPredecessor(node, predecessor gr.Node) bool {
|
||||
id := node.ID()
|
||||
pred := predecessor.ID()
|
||||
if _, ok := graph.successors[id]; !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
_, ok := graph.predecessors[id][pred]
|
||||
|
||||
return ok
|
||||
}
|
||||
|
||||
func (graph *GonumGraph) Neighbors(node gr.Node) []gr.Node {
|
||||
id := node.ID()
|
||||
if _, ok := graph.successors[id]; !ok {
|
||||
func (gr *Graph) Neighbors(node graph.Node) []graph.Node {
|
||||
if _, ok := gr.successors[node.ID()]; !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
neighbors := make([]gr.Node, 0, len(graph.predecessors[id])+len(graph.successors[id]))
|
||||
for succ, _ := range graph.successors[id] {
|
||||
neighbors = append(neighbors, graph.nodeMap[succ])
|
||||
neighbors := make([]graph.Node, len(gr.predecessors[node.ID()])+len(gr.successors[node.ID()]))
|
||||
i := 0
|
||||
for succ, _ := range gr.successors[node.ID()] {
|
||||
neighbors[i] = gr.nodeMap[succ]
|
||||
i++
|
||||
}
|
||||
|
||||
for pred, _ := range graph.predecessors[id] {
|
||||
for pred, _ := range gr.predecessors[node.ID()] {
|
||||
// We should only add the predecessor if it wasn't already added from successors
|
||||
if _, ok := graph.successors[id][pred]; !ok {
|
||||
neighbors = append(neighbors, graph.nodeMap[pred])
|
||||
if _, ok := gr.successors[node.ID()][pred]; !ok {
|
||||
neighbors[i] = gr.nodeMap[pred]
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
return neighbors
|
||||
}
|
||||
|
||||
func (graph *GonumGraph) IsNeighbor(node, neigh gr.Node) bool {
|
||||
id := node.ID()
|
||||
neighbor := neigh.ID()
|
||||
if _, ok := graph.successors[id]; !ok {
|
||||
return false
|
||||
func (gr *Graph) EdgeBetween(node, neigh graph.Node) graph.Edge {
|
||||
e := gr.EdgeTo(node, neigh)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
|
||||
_, succ := graph.predecessors[id][neighbor]
|
||||
_, pred := graph.predecessors[id][neighbor]
|
||||
e = gr.EdgeTo(neigh, node)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
|
||||
return succ || pred
|
||||
return nil
|
||||
}
|
||||
|
||||
func (graph *GonumGraph) NodeExists(node gr.Node) bool {
|
||||
_, ok := graph.successors[node.ID()]
|
||||
func (gr *Graph) NodeExists(node graph.Node) bool {
|
||||
_, ok := gr.nodeMap[node.ID()]
|
||||
|
||||
return ok
|
||||
}
|
||||
|
||||
func (graph *GonumGraph) Degree(node gr.Node) int {
|
||||
id := node.ID()
|
||||
if _, ok := graph.successors[id]; !ok {
|
||||
func (gr *Graph) Degree(node graph.Node) int {
|
||||
if _, ok := gr.nodeMap[node.ID()]; !ok {
|
||||
return 0
|
||||
}
|
||||
|
||||
return len(graph.successors[id]) + len(graph.predecessors[id])
|
||||
return len(gr.successors[node.ID()]) + len(gr.predecessors[node.ID()])
|
||||
}
|
||||
|
||||
func (graph *GonumGraph) EdgeList() []gr.Edge {
|
||||
eList := make([]gr.Edge, 0, len(graph.successors))
|
||||
for id, succMap := range graph.successors {
|
||||
for succ, _ := range succMap {
|
||||
eList = append(eList, GonumEdge{graph.nodeMap[id], graph.nodeMap[succ]})
|
||||
}
|
||||
}
|
||||
|
||||
return eList
|
||||
}
|
||||
|
||||
func (graph *GonumGraph) NodeList() []gr.Node {
|
||||
nodes := make([]gr.Node, 0, len(graph.successors))
|
||||
for _, node := range graph.nodeMap {
|
||||
nodes = append(nodes, node)
|
||||
func (gr *Graph) NodeList() []graph.Node {
|
||||
nodes := make([]graph.Node, len(gr.successors))
|
||||
i := 0
|
||||
for _, node := range gr.nodeMap {
|
||||
nodes[i] = node
|
||||
i++
|
||||
}
|
||||
|
||||
return nodes
|
||||
}
|
||||
|
||||
func (graph *GonumGraph) IsDirected() bool {
|
||||
return graph.directed
|
||||
}
|
||||
|
||||
func (graph *GonumGraph) Cost(node, succ gr.Node) float64 {
|
||||
if s, ok := graph.successors[node.ID()]; ok {
|
||||
if c, ok := s[succ.ID()]; ok {
|
||||
return c
|
||||
func (gr *Graph) Cost(e graph.Edge) float64 {
|
||||
if s, ok := gr.successors[e.Head().ID()]; ok {
|
||||
if we, ok := s[e.Tail().ID()]; ok {
|
||||
return we.Cost
|
||||
}
|
||||
}
|
||||
return math.Inf(1)
|
||||
}
|
||||
|
||||
func (gr *Graph) EdgeList() []graph.Edge {
|
||||
edgeList := make([]graph.Edge, 0, len(gr.successors))
|
||||
edgeMap := make(map[int]map[int]struct{}, len(gr.successors))
|
||||
for node, succMap := range gr.successors {
|
||||
edgeMap[node] = make(map[int]struct{}, len(succMap))
|
||||
for succ, edge := range succMap {
|
||||
if doneMap, ok := edgeMap[succ]; ok {
|
||||
if _, ok := doneMap[node]; ok {
|
||||
continue
|
||||
}
|
||||
}
|
||||
edgeList = append(edgeList, edge)
|
||||
edgeMap[node][succ] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
return edgeList
|
||||
}
|
||||
|
@@ -1,80 +1,14 @@
|
||||
package concrete_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
_ "testing"
|
||||
|
||||
gr "github.com/gonum/graph"
|
||||
"github.com/gonum/graph"
|
||||
"github.com/gonum/graph/concrete"
|
||||
)
|
||||
|
||||
var _ gr.Graph = &concrete.GonumGraph{}
|
||||
var _ gr.DirectedGraph = &concrete.GonumGraph{}
|
||||
var _ gr.MutableGraph = &concrete.GonumGraph{}
|
||||
var _ gr.EdgeListGraph = &concrete.GonumGraph{}
|
||||
var _ graph.Graph = &concrete.Graph{}
|
||||
var _ graph.DirectedGraph = &concrete.Graph{}
|
||||
var _ graph.MutableGraph = &concrete.Graph{}
|
||||
|
||||
// exercise methods for 100% coverage.
|
||||
func TestGonumGraphCoverage(t *testing.T) {
|
||||
var n1 gr.Node = concrete.GonumNode(1)
|
||||
var e gr.Edge = concrete.GonumEdge{n1, n1}
|
||||
e.Head()
|
||||
e.Tail()
|
||||
concrete.NewPreAllocatedGonumGraph(true, 1)
|
||||
dg := concrete.NewGonumGraph(true) // directed
|
||||
var dm gr.MutableGraph = dg // directed mutable
|
||||
var um gr.MutableGraph = concrete.NewGonumGraph(false) // undirected
|
||||
dm.AddNode(n1, nil)
|
||||
n0 := dm.NewNode(nil) // fill in hole, node 0
|
||||
dm.NewNode(nil) // node 2
|
||||
dm.AddNode(n1, nil) // no op
|
||||
um.AddNode(n1, nil) // undirected
|
||||
n3 := concrete.GonumNode(3)
|
||||
n4 := concrete.GonumNode(4)
|
||||
dm.AddNode(n3, []gr.Node{n4}) // both node and successor are new
|
||||
um.AddNode(n0, []gr.Node{n1}) // new undirected edge
|
||||
dm.AddEdge(e)
|
||||
n5 := concrete.GonumNode(5)
|
||||
dm.AddEdge(concrete.GonumEdge{n5, n1}) // head not in graph
|
||||
dm.AddEdge(concrete.GonumEdge{n1, n5}) // tail not in graph
|
||||
um.AddEdge(concrete.GonumEdge{n1, n0}) // undirected
|
||||
n6 := concrete.GonumNode(6)
|
||||
dm.SetEdgeCost(concrete.GonumEdge{n6, n1}, 0) // n6 not in graph
|
||||
dm.SetEdgeCost(concrete.GonumEdge{n1, n6}, 0)
|
||||
um.SetEdgeCost(concrete.GonumEdge{n0, n1}, 0) // undirected
|
||||
dm.AddEdge(concrete.GonumEdge{n5, n0})
|
||||
dm.RemoveNode(n5)
|
||||
dm.RemoveNode(n5)
|
||||
dm.RemoveEdge(concrete.GonumEdge{n6, n0})
|
||||
dm.RemoveEdge(concrete.GonumEdge{n0, n6})
|
||||
um.RemoveEdge(concrete.GonumEdge{n0, n1})
|
||||
dm.SetDirected(false)
|
||||
dm.EmptyGraph()
|
||||
dm.EmptyGraph()
|
||||
dm.SetDirected(true)
|
||||
d := dm.(gr.DirectedGraph)
|
||||
d.Successors(n0)
|
||||
dm.AddNode(n0, []gr.Node{n1})
|
||||
d.Successors(n0)
|
||||
d.IsSuccessor(n3, n1)
|
||||
d.IsSuccessor(n1, n3)
|
||||
d.Predecessors(n3)
|
||||
d.Predecessors(n1)
|
||||
d.IsPredecessor(n3, n1)
|
||||
d.IsPredecessor(n1, n3)
|
||||
u := um.(gr.Graph)
|
||||
u.Neighbors(n3)
|
||||
um.AddEdge(concrete.GonumEdge{n0, n1})
|
||||
u.Neighbors(n1)
|
||||
dg.Neighbors(n1) // directed graph neighbors
|
||||
d.IsNeighbor(n3, n1)
|
||||
d.IsNeighbor(n1, n3)
|
||||
d.NodeExists(n1)
|
||||
d.Degree(n3)
|
||||
um.AddEdge(concrete.GonumEdge{n0, n0})
|
||||
d.Degree(n0)
|
||||
u.Degree(n0)
|
||||
u.NodeList()
|
||||
u.(gr.EdgeLister).EdgeList()
|
||||
dg.IsDirected()
|
||||
dm.Cost(n0, n1)
|
||||
dm.Cost(n6, n1)
|
||||
}
|
||||
// var _ gr.EdgeListGraph = &concrete.Graph{}
|
||||
|
@@ -5,7 +5,7 @@ import (
|
||||
"math"
|
||||
"strings"
|
||||
|
||||
gr "github.com/gonum/graph"
|
||||
"github.com/gonum/graph"
|
||||
)
|
||||
|
||||
type TileGraph struct {
|
||||
@@ -68,20 +68,20 @@ func GenerateTileGraph(template string) (*TileGraph, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (graph *TileGraph) SetPassability(row, col int, passability bool) {
|
||||
loc := row*graph.numCols + col
|
||||
if loc >= len(graph.tiles) || row < 0 || col < 0 {
|
||||
func (gr *TileGraph) SetPassability(row, col int, passability bool) {
|
||||
loc := row*gr.numCols + col
|
||||
if loc >= len(gr.tiles) || row < 0 || col < 0 {
|
||||
return
|
||||
}
|
||||
|
||||
graph.tiles[loc] = passability
|
||||
gr.tiles[loc] = passability
|
||||
}
|
||||
|
||||
func (graph *TileGraph) String() string {
|
||||
func (gr *TileGraph) String() string {
|
||||
var outString string
|
||||
for r := 0; r < graph.numRows; r++ {
|
||||
for c := 0; c < graph.numCols; c++ {
|
||||
if graph.tiles[r*graph.numCols+c] == false {
|
||||
for r := 0; r < gr.numRows; r++ {
|
||||
for c := 0; c < gr.numCols; c++ {
|
||||
if gr.tiles[r*gr.numCols+c] == false {
|
||||
outString += "\u2580" // Black square
|
||||
} else {
|
||||
outString += " " // Space
|
||||
@@ -94,15 +94,15 @@ func (graph *TileGraph) String() string {
|
||||
return outString[:len(outString)-1] // Kill final newline
|
||||
}
|
||||
|
||||
func (graph *TileGraph) PathString(path []gr.Node) string {
|
||||
func (gr *TileGraph) PathString(path []graph.Node) string {
|
||||
if path == nil || len(path) == 0 {
|
||||
return graph.String()
|
||||
return gr.String()
|
||||
}
|
||||
|
||||
var outString string
|
||||
for r := 0; r < graph.numRows; r++ {
|
||||
for c := 0; c < graph.numCols; c++ {
|
||||
if id := r*graph.numCols + c; graph.tiles[id] == false {
|
||||
for r := 0; r < gr.numRows; r++ {
|
||||
for c := 0; c < gr.numCols; c++ {
|
||||
if id := r*gr.numCols + c; gr.tiles[id] == false {
|
||||
outString += "\u2580" // Black square
|
||||
} else if id == path[0].ID() {
|
||||
outString += "s"
|
||||
@@ -125,47 +125,47 @@ func (graph *TileGraph) PathString(path []gr.Node) string {
|
||||
return outString[:len(outString)-1]
|
||||
}
|
||||
|
||||
func (graph *TileGraph) Dimensions() (rows, cols int) {
|
||||
return graph.numRows, graph.numCols
|
||||
func (gr *TileGraph) Dimensions() (rows, cols int) {
|
||||
return gr.numRows, gr.numCols
|
||||
}
|
||||
|
||||
func (graph *TileGraph) IDToCoords(id int) (row, col int) {
|
||||
col = (id % graph.numCols)
|
||||
row = (id - col) / graph.numCols
|
||||
func (gr *TileGraph) IDToCoords(id int) (row, col int) {
|
||||
col = (id % gr.numCols)
|
||||
row = (id - col) / gr.numCols
|
||||
|
||||
return row, col
|
||||
}
|
||||
|
||||
func (graph *TileGraph) CoordsToID(row, col int) (id int) {
|
||||
if row < 0 || row >= graph.numRows || col < 0 || col >= graph.numCols {
|
||||
func (gr *TileGraph) CoordsToID(row, col int) (id int) {
|
||||
if row < 0 || row >= gr.numRows || col < 0 || col >= gr.numCols {
|
||||
return -1
|
||||
}
|
||||
id = row*graph.numCols + col
|
||||
id = row*gr.numCols + col
|
||||
|
||||
return id
|
||||
}
|
||||
|
||||
func (graph *TileGraph) CoordsToNode(row, col int) (node gr.Node) {
|
||||
id := graph.CoordsToID(row, col)
|
||||
func (gr *TileGraph) CoordsToNode(row, col int) (node graph.Node) {
|
||||
id := gr.CoordsToID(row, col)
|
||||
if id == -1 {
|
||||
return nil
|
||||
} else {
|
||||
return GonumNode(id)
|
||||
return Node(id)
|
||||
}
|
||||
}
|
||||
|
||||
func (graph *TileGraph) successors(node gr.Node) []gr.Node {
|
||||
func (gr *TileGraph) Neighbors(node graph.Node) []graph.Node {
|
||||
id := node.ID()
|
||||
if id < 0 || id >= len(graph.tiles) || graph.tiles[id] == false {
|
||||
if !gr.NodeExists(node) {
|
||||
return nil
|
||||
}
|
||||
|
||||
row, col := graph.IDToCoords(id)
|
||||
row, col := gr.IDToCoords(id)
|
||||
|
||||
neighbors := []gr.Node{graph.CoordsToNode(row-1, col), graph.CoordsToNode(row+1, col), graph.CoordsToNode(row, col-1), graph.CoordsToNode(row, col+1)}
|
||||
realNeighbors := make([]gr.Node, 0, 4) // Will overallocate sometimes, but not by much. Not a big deal
|
||||
neighbors := []graph.Node{gr.CoordsToNode(row-1, col), gr.CoordsToNode(row+1, col), gr.CoordsToNode(row, col-1), gr.CoordsToNode(row, col+1)}
|
||||
realNeighbors := make([]graph.Node, 0, 4) // Will overallocate sometimes, but not by much. Not a big deal
|
||||
for _, neighbor := range neighbors {
|
||||
if neighbor != nil && graph.tiles[neighbor.ID()] == true {
|
||||
if neighbor != nil && gr.tiles[neighbor.ID()] == true {
|
||||
realNeighbors = append(realNeighbors, neighbor)
|
||||
}
|
||||
}
|
||||
@@ -173,72 +173,61 @@ func (graph *TileGraph) successors(node gr.Node) []gr.Node {
|
||||
return realNeighbors
|
||||
}
|
||||
|
||||
func (graph *TileGraph) isSuccessor(node, successor gr.Node) bool {
|
||||
id, succ := node.ID(), successor.ID()
|
||||
return (id >= 0 && id < len(graph.tiles) && graph.tiles[id] == true) && (succ >= 0 && succ < len(graph.tiles) && graph.tiles[succ] == true)
|
||||
func (gr *TileGraph) EdgeBetween(node, neighbor graph.Node) graph.Edge {
|
||||
if !gr.NodeExists(node) || !gr.NodeExists(neighbor) {
|
||||
return nil
|
||||
}
|
||||
|
||||
r1, c1 := gr.IDToCoords(node.ID())
|
||||
r2, c2 := gr.IDToCoords(neighbor.ID())
|
||||
if (c1 == c2 && (r2 == r1+1 || r2 == r1-1)) || (r1 == r2 && (c2 == c1+1 || c2 == c1-1)) {
|
||||
return Edge{node, neighbor}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (graph *TileGraph) predecessors(node gr.Node) []gr.Node {
|
||||
return graph.successors(node)
|
||||
}
|
||||
|
||||
func (graph *TileGraph) isPredecessor(node, pred gr.Node) bool {
|
||||
return graph.isSuccessor(node, pred)
|
||||
}
|
||||
|
||||
func (graph *TileGraph) Neighbors(node gr.Node) []gr.Node {
|
||||
return graph.successors(node)
|
||||
}
|
||||
|
||||
func (graph *TileGraph) IsNeighbor(id, neighbor gr.Node) bool {
|
||||
return graph.isSuccessor(id, neighbor)
|
||||
}
|
||||
|
||||
func (graph *TileGraph) NodeExists(node gr.Node) bool {
|
||||
func (gr *TileGraph) NodeExists(node graph.Node) bool {
|
||||
id := node.ID()
|
||||
return id >= 0 && id < len(graph.tiles) && graph.tiles[id] == true
|
||||
return id >= 0 && id < len(gr.tiles) && gr.tiles[id] == true
|
||||
}
|
||||
|
||||
func (graph *TileGraph) Degree(node gr.Node) int {
|
||||
return len(graph.successors(node)) * 2
|
||||
func (gr *TileGraph) Degree(node graph.Node) int {
|
||||
return len(gr.Neighbors(node)) * 2
|
||||
}
|
||||
|
||||
func (graph *TileGraph) EdgeList() []gr.Edge {
|
||||
edges := make([]gr.Edge, 0)
|
||||
for id, passable := range graph.tiles {
|
||||
func (gr *TileGraph) EdgeList() []graph.Edge {
|
||||
edges := make([]graph.Edge, 0)
|
||||
for id, passable := range gr.tiles {
|
||||
if !passable {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, succ := range graph.successors(GonumNode(id)) {
|
||||
edges = append(edges, GonumEdge{GonumNode(id), succ})
|
||||
for _, succ := range gr.Neighbors(Node(id)) {
|
||||
edges = append(edges, Edge{Node(id), succ})
|
||||
}
|
||||
}
|
||||
|
||||
return edges
|
||||
}
|
||||
|
||||
func (graph *TileGraph) NodeList() []gr.Node {
|
||||
nodes := make([]gr.Node, 0)
|
||||
for id, passable := range graph.tiles {
|
||||
func (gr *TileGraph) NodeList() []graph.Node {
|
||||
nodes := make([]graph.Node, 0)
|
||||
for id, passable := range gr.tiles {
|
||||
if !passable {
|
||||
continue
|
||||
}
|
||||
|
||||
nodes = append(nodes, GonumNode(id))
|
||||
nodes = append(nodes, Node(id))
|
||||
}
|
||||
|
||||
return nodes
|
||||
}
|
||||
|
||||
func (graph *TileGraph) IsDirected() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (graph *TileGraph) Cost(node1, node2 gr.Node) float64 {
|
||||
if graph.IsNeighbor(node1, node2) {
|
||||
func (gr *TileGraph) Cost(e graph.Edge) float64 {
|
||||
if edge := gr.EdgeBetween(e.Head(), e.Tail()); edge != nil {
|
||||
return 1.0
|
||||
} else {
|
||||
return math.Inf(1)
|
||||
}
|
||||
|
||||
return math.Inf(1)
|
||||
}
|
||||
|
@@ -3,11 +3,11 @@ package concrete_test
|
||||
import (
|
||||
"testing"
|
||||
|
||||
gr "github.com/gonum/graph"
|
||||
"github.com/gonum/graph"
|
||||
"github.com/gonum/graph/concrete"
|
||||
)
|
||||
|
||||
var _ gr.Graph = (*concrete.TileGraph)(nil)
|
||||
var _ graph.Graph = (*concrete.TileGraph)(nil)
|
||||
|
||||
func TestTileGraph(t *testing.T) {
|
||||
tg := concrete.NewTileGraph(4, 4, false)
|
||||
@@ -77,11 +77,11 @@ func TestTileGraph(t *testing.T) {
|
||||
t.Error("ID to Coords fails on 3,0")
|
||||
}
|
||||
|
||||
if succ := tg.Neighbors(concrete.GonumNode(0)); succ != nil || len(succ) != 0 {
|
||||
if succ := tg.Neighbors(concrete.Node(0)); succ != nil || len(succ) != 0 {
|
||||
t.Error("Successors for impassable tile not 0")
|
||||
}
|
||||
|
||||
if succ := tg.Neighbors(concrete.GonumNode(2)); succ == nil || len(succ) != 2 {
|
||||
if succ := tg.Neighbors(concrete.Node(2)); succ == nil || len(succ) != 2 {
|
||||
t.Error("Incorrect number of successors for (0,2)")
|
||||
} else {
|
||||
for _, s := range succ {
|
||||
@@ -91,45 +91,14 @@ func TestTileGraph(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
if tg.Degree(concrete.GonumNode(2)) != 4 {
|
||||
if tg.Degree(concrete.Node(2)) != 4 {
|
||||
t.Error("Degree returns incorrect number for (0,2)")
|
||||
}
|
||||
if tg.Degree(concrete.GonumNode(1)) != 2 {
|
||||
if tg.Degree(concrete.Node(1)) != 2 {
|
||||
t.Error("Degree returns incorrect number for (0,2)")
|
||||
}
|
||||
if tg.Degree(concrete.GonumNode(0)) != 0 {
|
||||
if tg.Degree(concrete.Node(0)) != 0 {
|
||||
t.Error("Degree returns incorrect number for impassable tile (0,0)")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestTileGraphCoverage(t *testing.T) {
|
||||
concrete.GenerateTileGraph("▀ x") // x invalid
|
||||
concrete.GenerateTileGraph("▀ \n▀") // row lengths not equal
|
||||
concrete.GenerateTileGraph("▀ ▀")
|
||||
tg := concrete.NewTileGraph(2, 3, true)
|
||||
tg.SetPassability(0, -1, true)
|
||||
tg.SetPassability(0, 1, false)
|
||||
tg.SetPassability(0, 4, false)
|
||||
tg.PathString(nil)
|
||||
n1 := concrete.GonumNode(1)
|
||||
n2 := concrete.GonumNode(2)
|
||||
n3 := concrete.GonumNode(3)
|
||||
n5 := concrete.GonumNode(5)
|
||||
p := []gr.Node{n3, n2, n1, n5}
|
||||
tg.PathString(p)
|
||||
tg.Dimensions()
|
||||
tg.IDToCoords(0)
|
||||
tg.CoordsToNode(-1, 0)
|
||||
tg.CoordsToNode(0, 0)
|
||||
tg.Neighbors(n1)
|
||||
tg.Neighbors(n2)
|
||||
tg.IsNeighbor(n1, n2)
|
||||
tg.NodeExists(n1)
|
||||
tg.Degree(n1)
|
||||
tg.EdgeList()
|
||||
tg.NodeList()
|
||||
tg.IsDirected()
|
||||
tg.Cost(n1, n2)
|
||||
tg.Cost(n2, n5)
|
||||
}
|
||||
|
115
graph.go
115
graph.go
@@ -15,51 +15,50 @@ type Edge interface {
|
||||
Tail() Node
|
||||
}
|
||||
|
||||
// A Graph ensures the behavior of an undirected graph, necessary to run certain algorithms on it.
|
||||
// A Graph implements the behavior of an undirected graph.
|
||||
//
|
||||
// The Graph interface is directed. This means that EdgeList() should return an edge where Head
|
||||
// always goes towards Tail. If your graph is undirected and you only maintain edges for one
|
||||
// direction, simply return two edges for each one of your edges, with the Head and Tail swapped
|
||||
// in each one.
|
||||
// All methods in Graph are implicitly undirected. Graph algorithms that care about directionality
|
||||
// will intelligently choose the DirectedGraph behavior if that interface is also implemented,
|
||||
// even if the function itself only takes in a Graph (or a super-interface of graph).
|
||||
type Graph interface {
|
||||
// NodeExists returns true when node is currently in the graph.
|
||||
NodeExists(node Node) bool
|
||||
// Degree is equivalent to len(Successors(node)) + len(Predecessors(node)).
|
||||
// This means that reflexive edges are counted twice.
|
||||
Degree(node Node) int
|
||||
|
||||
// NodeList returns a list of all nodes in no particular order, useful for
|
||||
// determining things like if a graph is fully connected. The caller is
|
||||
// free to modify this list. Implementations should construct a new list
|
||||
// and not return internal representation.
|
||||
NodeList() []Node
|
||||
|
||||
// Neighbors returns all nodes connected by any edge to this node.
|
||||
Neighbors(node Node) []Node
|
||||
// IsNeighbor returns true when neighbor is connected to node by an edge.
|
||||
IsNeighbor(node, neighbor Node) bool
|
||||
|
||||
// EdgeBetween returns an edge between node and neighbor such that
|
||||
// Head is one argument and Tail is the other. If no
|
||||
// such edge exists, this function returns nil.
|
||||
EdgeBetween(node, neighbor Node) Edge
|
||||
}
|
||||
|
||||
// Directed graphs are characterized by having seperable Heads and Tails in their edges.
|
||||
// That is, if node1 goes to node2, that does not necessarily imply that node2 goes to node1.
|
||||
//
|
||||
// While it's possible for a directed graph to have fully reciprocal edges (i.e. the graph is
|
||||
// symmetric) -- it is not required to be. The graph is also required to implement UndirectedGraph
|
||||
// because it can be useful to know all neighbors regardless of direction; not because this graph
|
||||
// treats directed graphs as special cases of undirected ones (the truth is, in fact, the opposite.)
|
||||
// symmetric) -- it is not required to be. The graph is also required to implement Graph
|
||||
// because in many cases it can be useful to know all neighbors regardless of direction.
|
||||
type DirectedGraph interface {
|
||||
Graph
|
||||
// Successors gives the nodes connected by OUTBOUND edges.
|
||||
// If the graph is an undirected graph, this set is equal to Predecessors.
|
||||
Successors(node Node) []Node
|
||||
// IsSuccessor returns true if successor shows up in the list returned by
|
||||
// Successors(node). If node doesn't exist, this should always return false.
|
||||
IsSuccessor(node, successor Node) bool
|
||||
|
||||
// EdgeTo returns an edge between node and successor such that
|
||||
// Head returns node and Tail returns successor, if no
|
||||
// such edge exists, this function returns nil.
|
||||
EdgeTo(node, successor Node) Edge
|
||||
|
||||
// Predecessors gives the nodes connected by INBOUND edges.
|
||||
// If the graph is an undirected graph, this set is equal to Successors.
|
||||
Predecessors(node Node) []Node
|
||||
// IsPredecessor returns true if predecessor shows up in the list returned
|
||||
// by Predecessors(node). If node doesn't exist, this should always return
|
||||
// false.
|
||||
IsPredecessor(node, predecessor Node) bool
|
||||
}
|
||||
|
||||
// Returns all undirected edges in the graph
|
||||
@@ -72,6 +71,7 @@ type EdgeListGraph interface {
|
||||
EdgeLister
|
||||
}
|
||||
|
||||
// Returns all directed edges in the graph.
|
||||
type DirectedEdgeLister interface {
|
||||
DirectedEdgeList() []Edge
|
||||
}
|
||||
@@ -82,9 +82,11 @@ type DirectedEdgeListGraph interface {
|
||||
}
|
||||
|
||||
// A crunch graph forces a sparse graph to become a dense graph. That is, if the node IDs are
|
||||
// [1,4,9,7] it would "crunch" the ids into the contiguous block [0,1,2,3].
|
||||
// [1,4,9,7] it would "crunch" the ids into the contiguous block [0,1,2,3]. Order is not
|
||||
// required to be preserved between the non-cruched and crunched instances (that means in
|
||||
// the example above 0 may correspond to 4 or 7 or 9, not necessarily 1).
|
||||
//
|
||||
// All dense graphs should have the first ID at 0.
|
||||
// All dense graphs must have the first ID as 0.
|
||||
type CrunchGraph interface {
|
||||
Graph
|
||||
Crunch()
|
||||
@@ -95,10 +97,9 @@ type CrunchGraph interface {
|
||||
// this function will take precedence over the Uniform Cost function (all weights are 1) if "nil"
|
||||
// is passed in for the function argument.
|
||||
//
|
||||
// If no edge exists between node1 and node2, the cost should be taken to be +inf (can be gotten
|
||||
// by math.Inf(1).)
|
||||
// If the argument is nil, or the edge is invalid for some reason, this should return math.Inf(1)
|
||||
type Coster interface {
|
||||
Cost(node1, node2 Node) float64
|
||||
Cost(edge Edge) float64
|
||||
}
|
||||
|
||||
// Guarantees that something implementing Coster is also a Graph.
|
||||
@@ -117,7 +118,7 @@ type HeuristicCoster interface {
|
||||
HeuristicCost(node1, node2 Node) float64
|
||||
}
|
||||
|
||||
// A Mutable Graph is a graph that can be changed in an arbitrary way. It is useful for several
|
||||
// A MutableGraph is a graph that can be changed in an arbitrary way. It is useful for several
|
||||
// algorithms; for instance, Johnson's Algorithm requires adding a temporary node and changing
|
||||
// edge weights. Another case where this is used is computing minimum spanning trees. Since trees
|
||||
// are graphs, a minimum spanning tree can be created using this interface.
|
||||
@@ -128,41 +129,42 @@ type HeuristicCoster interface {
|
||||
// properly handle the graph in order to, say, fill it with a minimum spanning tree.
|
||||
//
|
||||
// In functions that take a MutableGraph as an argument, it should not be the same as the Graph
|
||||
// argument as concurrent modification will likely cause problems in most cases.
|
||||
// argument as concurrent modification will likely cause problems.
|
||||
//
|
||||
// Mutable graphs should always record the IDs as they are represented -- which means they are
|
||||
// MutableGraphs should always record the IDs as they are represented -- which means they are
|
||||
// sparse by nature.
|
||||
//
|
||||
// MutableGraphs are required to keep the exact Nodes and Edges passed in, and return
|
||||
// the originals when asked.
|
||||
type MutableGraph interface {
|
||||
CostGraph
|
||||
// NewNode adds a node with an arbitrary ID and returns the new, unique ID
|
||||
// used.
|
||||
NewNode(successors []Node) Node
|
||||
// The graph itself is responsible for adding reciprocal edges if it's
|
||||
// undirected. Likewise, the graph itself must add any non-existant nodes
|
||||
// listed in successors.
|
||||
AddNode(node Node, successors []Node)
|
||||
// For a digraph, adds node1->node2; the graph is free to initialize this
|
||||
// to any value it wishes. Node1 must exist, or it will result in undefined
|
||||
// behavior. Node2 must be created by the function if absent.
|
||||
AddEdge(e Edge)
|
||||
// The behavior is undefined if the edge has not been created with AddEdge
|
||||
// (or the edge was removed before this function was called). For a
|
||||
// directed graph only sets node1->node2.
|
||||
SetEdgeCost(e Edge, cost float64)
|
||||
// The graph is reponsible for removing edges to a node that is removed.
|
||||
NewNode() Node
|
||||
|
||||
// Adds a node to the graph
|
||||
AddNode(node Node)
|
||||
|
||||
// AddEdge connects two nodes in the graph. Neither node is required
|
||||
// to have been added before this is called. If directed is false,
|
||||
// it also adds the reciprocal edge. If this is called a second time,
|
||||
// it overrides any existing edge.
|
||||
AddEdge(e Edge, cost float64, directed bool)
|
||||
|
||||
// RemoveNode removes a node from the graph, as well as any edges
|
||||
// attached to it
|
||||
RemoveNode(node Node)
|
||||
// The graph is responsible for removing reciprocal edges if it's
|
||||
// undirected.
|
||||
RemoveEdge(e Edge)
|
||||
|
||||
// RemoveEdge removes a connection between two nodes, but does not
|
||||
// remove Head nor Tail under any circumstance. As with AddEdge, if
|
||||
// directed is false it also removes the reciprocal edge. This function
|
||||
// should be treated as a no-op and not an error if the edge doesn't exist.
|
||||
RemoveEdge(e Edge, directed bool)
|
||||
|
||||
// EmptyGraph clears the graph of all nodes and edges.
|
||||
EmptyGraph()
|
||||
// This package will only call SetDirected on an empty graph, so there's no
|
||||
// need to worry about the case where a graph suddenly becomes (un)directed.
|
||||
SetDirected(bool)
|
||||
}
|
||||
|
||||
// TODO AddNode, AddEdge, SetEdgeCost, RemoveNode, RemoveEdge, SetDirected need to say what they do.
|
||||
|
||||
// A DStarGraph is a special interface that allows the DStarLite function to be used on a graph.
|
||||
//
|
||||
// D*-lite is an algorithm that allows for the graph representation to change when actions are
|
||||
@@ -185,5 +187,14 @@ type DStarGraph interface {
|
||||
ChangedEdges() (newCostFunc func(Node, Node) float64, changedEdges []Edge)
|
||||
}
|
||||
|
||||
// A function that returns the cost from one node to another.
|
||||
type CostFunc func(Node, Node) float64
|
||||
// A function that returns the cost of following an edge
|
||||
type CostFunc func(Edge) float64
|
||||
|
||||
// Estimates the cost of travelling between two nodes
|
||||
type HeuristicCostFunc func(Node, Node) float64
|
||||
|
||||
// Convenience constants for AddEdge and RemoveEdge
|
||||
const (
|
||||
Directed bool = true
|
||||
Undirected = false
|
||||
)
|
||||
|
@@ -11,7 +11,7 @@ func TestFWOneEdge(t *testing.T) {
|
||||
dg := concrete.NewDenseGraph(2, true)
|
||||
aPaths, sPath := search.FloydWarshall(dg, nil)
|
||||
|
||||
path, cost, err := sPath(concrete.GonumNode(0), concrete.GonumNode(1))
|
||||
path, cost, err := sPath(concrete.Node(0), concrete.Node(1))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -24,7 +24,7 @@ func TestFWOneEdge(t *testing.T) {
|
||||
t.Errorf("Wrong path in FW %v", path)
|
||||
}
|
||||
|
||||
paths, cost, err := aPaths(concrete.GonumNode(0), concrete.GonumNode(1))
|
||||
paths, cost, err := aPaths(concrete.Node(0), concrete.Node(1))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -46,12 +46,12 @@ func TestFWOneEdge(t *testing.T) {
|
||||
func TestFWTwoPaths(t *testing.T) {
|
||||
dg := concrete.NewDenseGraph(5, false)
|
||||
// Adds two paths from 0->2 of equal length
|
||||
dg.SetEdgeCost(concrete.GonumNode(0), concrete.GonumNode(2), 2.0, true)
|
||||
dg.SetEdgeCost(concrete.GonumNode(0), concrete.GonumNode(1), 1.0, true)
|
||||
dg.SetEdgeCost(concrete.GonumNode(1), concrete.GonumNode(2), 1.0, true)
|
||||
dg.SetEdgeCost(concrete.Edge{concrete.Node(0), concrete.Node(2)}, 2.0, true)
|
||||
dg.SetEdgeCost(concrete.Edge{concrete.Node(0), concrete.Node(1)}, 1.0, true)
|
||||
dg.SetEdgeCost(concrete.Edge{concrete.Node(1), concrete.Node(2)}, 1.0, true)
|
||||
|
||||
aPaths, sPath := search.FloydWarshall(dg, nil)
|
||||
path, cost, err := sPath(concrete.GonumNode(0), concrete.GonumNode(2))
|
||||
path, cost, err := sPath(concrete.Node(0), concrete.Node(2))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -68,7 +68,7 @@ func TestFWTwoPaths(t *testing.T) {
|
||||
t.Errorf("Got wrong path %v", path)
|
||||
}
|
||||
|
||||
paths, cost, err := aPaths(concrete.GonumNode(0), concrete.GonumNode(2))
|
||||
paths, cost, err := aPaths(concrete.Node(0), concrete.Node(2))
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -99,26 +99,26 @@ func TestFWConfoundingPath(t *testing.T) {
|
||||
dg := concrete.NewDenseGraph(6, false)
|
||||
|
||||
// Add a path from 0->5 of cost 4
|
||||
dg.SetEdgeCost(concrete.GonumNode(0), concrete.GonumNode(1), 1.0, true)
|
||||
dg.SetEdgeCost(concrete.GonumNode(1), concrete.GonumNode(2), 1.0, true)
|
||||
dg.SetEdgeCost(concrete.GonumNode(2), concrete.GonumNode(3), 1.0, true)
|
||||
dg.SetEdgeCost(concrete.GonumNode(3), concrete.GonumNode(5), 1.0, true)
|
||||
dg.SetEdgeCost(concrete.Edge{concrete.Node(0), concrete.Node(1)}, 1.0, true)
|
||||
dg.SetEdgeCost(concrete.Edge{concrete.Node(1), concrete.Node(2)}, 1.0, true)
|
||||
dg.SetEdgeCost(concrete.Edge{concrete.Node(2), concrete.Node(3)}, 1.0, true)
|
||||
dg.SetEdgeCost(concrete.Edge{concrete.Node(3), concrete.Node(5)}, 1.0, true)
|
||||
|
||||
// Add direct edge to goal of cost 4
|
||||
dg.SetEdgeCost(concrete.GonumNode(0), concrete.GonumNode(5), 4.0, true)
|
||||
dg.SetEdgeCost(concrete.Edge{concrete.Node(0), concrete.Node(5)}, 4.0, true)
|
||||
|
||||
// Add edge to a node that's still optimal
|
||||
dg.SetEdgeCost(concrete.GonumNode(0), concrete.GonumNode(2), 2.0, true)
|
||||
dg.SetEdgeCost(concrete.Edge{concrete.Node(0), concrete.Node(2)}, 2.0, true)
|
||||
|
||||
// Add edge to 3 that's overpriced
|
||||
dg.SetEdgeCost(concrete.GonumNode(0), concrete.GonumNode(3), 4.0, true)
|
||||
dg.SetEdgeCost(concrete.Edge{concrete.Node(0), concrete.Node(3)}, 4.0, true)
|
||||
|
||||
// Add very cheap edge to 4 which is a dead end
|
||||
dg.SetEdgeCost(concrete.GonumNode(0), concrete.GonumNode(4), 0.25, true)
|
||||
dg.SetEdgeCost(concrete.Edge{concrete.Node(0), concrete.Node(4)}, 0.25, true)
|
||||
|
||||
aPaths, sPath := search.FloydWarshall(dg, nil)
|
||||
|
||||
path, cost, err := sPath(concrete.GonumNode(0), concrete.GonumNode(5))
|
||||
path, cost, err := sPath(concrete.Node(0), concrete.Node(5))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -137,7 +137,7 @@ func TestFWConfoundingPath(t *testing.T) {
|
||||
t.Errorf("Wrong path found for single path %v", path)
|
||||
}
|
||||
|
||||
paths, cost, err := aPaths(concrete.GonumNode(0), concrete.GonumNode(5))
|
||||
paths, cost, err := aPaths(concrete.Node(0), concrete.Node(5))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -162,14 +162,14 @@ func TestFWConfoundingPath(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
path, _, err = sPath(concrete.GonumNode(4), concrete.GonumNode(5))
|
||||
path, _, err = sPath(concrete.Node(4), concrete.Node(5))
|
||||
if err != nil {
|
||||
t.Log("Success!", err)
|
||||
} else {
|
||||
t.Errorf("Path was found by FW single path where one shouldn't be %v", path)
|
||||
}
|
||||
|
||||
paths, _, err = aPaths(concrete.GonumNode(4), concrete.GonumNode(5))
|
||||
paths, _, err = aPaths(concrete.Node(4), concrete.Node(5))
|
||||
if err != nil {
|
||||
t.Log("Success!", err)
|
||||
} else {
|
||||
|
@@ -5,14 +5,14 @@ import (
|
||||
"math"
|
||||
"sort"
|
||||
|
||||
gr "github.com/gonum/graph"
|
||||
"github.com/gonum/graph"
|
||||
)
|
||||
|
||||
// Finds all shortest paths between start and goal
|
||||
type AllPathFunc func(start, goal gr.Node) (path [][]gr.Node, cost float64, err error)
|
||||
type AllPathFunc func(start, goal graph.Node) (path [][]graph.Node, cost float64, err error)
|
||||
|
||||
// Finds one path between start and goal, which it finds is arbitrary
|
||||
type PathFunc func(start, goal gr.Node) (path []gr.Node, cost float64, err error)
|
||||
type PathFunc func(start, goal graph.Node) (path []graph.Node, cost float64, err error)
|
||||
|
||||
// This function returns two functions: one that will generate all shortest paths between two
|
||||
// nodes with ids i and j, and one that will generate just one path.
|
||||
@@ -32,12 +32,12 @@ type PathFunc func(start, goal gr.Node) (path []gr.Node, cost float64, err error
|
||||
// The other will return the cost and an error if no path exists, but it will also return ALL
|
||||
// possible shortest paths between start and goal. This is not too much more expensive than
|
||||
// generating one path, but it does obviously increase with the number of paths.
|
||||
func FloydWarshall(graph gr.CrunchGraph, cost gr.CostFunc) (AllPathFunc, PathFunc) {
|
||||
graph.Crunch()
|
||||
sf := setupFuncs(graph, cost, nil)
|
||||
successors, isSuccessor, cost := sf.successors, sf.isSuccessor, sf.cost
|
||||
func FloydWarshall(gr graph.CrunchGraph, cost graph.CostFunc) (AllPathFunc, PathFunc) {
|
||||
gr.Crunch()
|
||||
sf := setupFuncs(gr, cost, nil)
|
||||
successors, isSuccessor, cost, edgeTo := sf.successors, sf.isSuccessor, sf.cost, sf.edgeTo
|
||||
|
||||
nodes := denseNodeSorter(graph.NodeList())
|
||||
nodes := denseNodeSorter(gr.NodeList())
|
||||
sort.Sort(nodes)
|
||||
numNodes := len(nodes)
|
||||
|
||||
@@ -53,7 +53,7 @@ func FloydWarshall(graph gr.CrunchGraph, cost gr.CostFunc) (AllPathFunc, PathFun
|
||||
|
||||
for _, node := range nodes {
|
||||
for _, succ := range successors(node) {
|
||||
dist[node.ID()+succ.ID()*numNodes] = cost(node, succ)
|
||||
dist[node.ID()+succ.ID()*numNodes] = cost(edgeTo(node, succ))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,29 +80,29 @@ func FloydWarshall(graph gr.CrunchGraph, cost gr.CostFunc) (AllPathFunc, PathFun
|
||||
}
|
||||
}
|
||||
|
||||
return genAllPathsFunc(dist, next, nodes, graph, cost, isSuccessor), genSinglePathFunc(dist, next, nodes)
|
||||
return genAllPathsFunc(dist, next, nodes, gr, cost, isSuccessor, edgeTo), genSinglePathFunc(dist, next, nodes)
|
||||
}
|
||||
|
||||
func genAllPathsFunc(dist []float64, next [][]int, nodes []gr.Node, graph gr.Graph, cost func(gr.Node, gr.Node) float64, isSuccessor func(gr.Node, gr.Node) bool) func(start, goal gr.Node) ([][]gr.Node, float64, error) {
|
||||
func genAllPathsFunc(dist []float64, next [][]int, nodes []graph.Node, gr graph.Graph, cost graph.CostFunc, isSuccessor func(graph.Node, graph.Node) bool, edgeTo func(graph.Node, graph.Node) graph.Edge) func(start, goal graph.Node) ([][]graph.Node, float64, error) {
|
||||
numNodes := len(nodes)
|
||||
|
||||
// A recursive function to reconstruct all possible paths.
|
||||
// It's not fast, but it's about as fast as can be reasonably expected
|
||||
var allPathFinder func(i, j int) ([][]gr.Node, error)
|
||||
allPathFinder = func(i, j int) ([][]gr.Node, error) {
|
||||
var allPathFinder func(i, j int) ([][]graph.Node, error)
|
||||
allPathFinder = func(i, j int) ([][]graph.Node, error) {
|
||||
if dist[i+j*numNodes] == math.Inf(1) {
|
||||
return nil, errors.New("No path")
|
||||
}
|
||||
intermediates := next[i+j*numNodes]
|
||||
if intermediates == nil || len(intermediates) == 0 {
|
||||
// There is exactly one path
|
||||
return [][]gr.Node{[]gr.Node{}}, nil
|
||||
return [][]graph.Node{[]graph.Node{}}, nil
|
||||
}
|
||||
|
||||
toReturn := make([][]gr.Node, 0, len(intermediates))
|
||||
toReturn := make([][]graph.Node, 0, len(intermediates))
|
||||
// Special case: if intermediates exist we need to explicitly check to see if i and j is also an optimal path
|
||||
if isSuccessor(nodes[i], nodes[j]) && math.Abs(dist[i+j*numNodes]-cost(nodes[i], nodes[j])) < .000001 {
|
||||
toReturn = append(toReturn, []gr.Node{})
|
||||
if isSuccessor(nodes[i], nodes[j]) && math.Abs(dist[i+j*numNodes]-cost(edgeTo(nodes[i], nodes[j]))) < .000001 {
|
||||
toReturn = append(toReturn, []graph.Node{})
|
||||
}
|
||||
|
||||
// This step is a tad convoluted: we have some list of intermediates.
|
||||
@@ -139,7 +139,7 @@ func genAllPathsFunc(dist []float64, next [][]int, nodes []gr.Node, graph gr.Gra
|
||||
// (the copying stuff is because slices are reference types)
|
||||
for a := range succs {
|
||||
for b := range preds {
|
||||
path := make([]gr.Node, len(succs[a]), len(succs[a])+len(preds[b]))
|
||||
path := make([]graph.Node, len(succs[a]), len(succs[a])+len(preds[b]))
|
||||
copy(path, succs[a])
|
||||
path = append(path, preds[b]...)
|
||||
toReturn = append(toReturn, path)
|
||||
@@ -151,7 +151,7 @@ func genAllPathsFunc(dist []float64, next [][]int, nodes []gr.Node, graph gr.Gra
|
||||
return toReturn, nil
|
||||
}
|
||||
|
||||
return func(start, goal gr.Node) ([][]gr.Node, float64, error) {
|
||||
return func(start, goal graph.Node) ([][]graph.Node, float64, error) {
|
||||
paths, err := allPathFinder(start.ID(), goal.ID())
|
||||
if err != nil {
|
||||
return nil, math.Inf(1), err
|
||||
@@ -179,18 +179,18 @@ func genAllPathsFunc(dist []float64, next [][]int, nodes []gr.Node, graph gr.Gra
|
||||
}
|
||||
}
|
||||
|
||||
func genSinglePathFunc(dist []float64, next [][]int, nodes []gr.Node) func(start, goal gr.Node) ([]gr.Node, float64, error) {
|
||||
func genSinglePathFunc(dist []float64, next [][]int, nodes []graph.Node) func(start, goal graph.Node) ([]graph.Node, float64, error) {
|
||||
numNodes := len(nodes)
|
||||
|
||||
var singlePathFinder func(i, j int) ([]gr.Node, error)
|
||||
singlePathFinder = func(i, j int) ([]gr.Node, error) {
|
||||
var singlePathFinder func(i, j int) ([]graph.Node, error)
|
||||
singlePathFinder = func(i, j int) ([]graph.Node, error) {
|
||||
if dist[i+j*numNodes] == math.Inf(1) {
|
||||
return nil, errors.New("No path")
|
||||
}
|
||||
|
||||
intermediates := next[i+j*numNodes]
|
||||
if intermediates == nil || len(intermediates) == 0 {
|
||||
return []gr.Node{}, nil
|
||||
return []graph.Node{}, nil
|
||||
}
|
||||
|
||||
intermediate := intermediates[0]
|
||||
@@ -209,7 +209,7 @@ func genSinglePathFunc(dist []float64, next [][]int, nodes []gr.Node) func(start
|
||||
return path, nil
|
||||
}
|
||||
|
||||
return func(start, goal gr.Node) ([]gr.Node, float64, error) {
|
||||
return func(start, goal graph.Node) ([]graph.Node, float64, error) {
|
||||
path, err := singlePathFinder(start.ID(), goal.ID())
|
||||
if err != nil {
|
||||
return nil, math.Inf(1), err
|
||||
|
@@ -6,7 +6,7 @@ import (
|
||||
"sort"
|
||||
|
||||
"errors"
|
||||
gr "github.com/gonum/graph"
|
||||
"github.com/gonum/graph"
|
||||
"github.com/gonum/graph/concrete"
|
||||
"github.com/gonum/graph/set"
|
||||
"github.com/gonum/graph/xifo"
|
||||
@@ -40,16 +40,16 @@ import (
|
||||
//
|
||||
// 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 gr.CostFunc) (path []gr.Node, pathCost float64, nodesExpanded int) {
|
||||
sf := setupFuncs(graph, cost, heuristicCost)
|
||||
successors, cost, heuristicCost := sf.successors, sf.cost, sf.heuristicCost
|
||||
func AStar(start, goal graph.Node, gr graph.Graph, cost graph.CostFunc, heuristicCost graph.HeuristicCostFunc) (path []graph.Node, pathCost float64, nodesExpanded int) {
|
||||
sf := setupFuncs(gr, cost, heuristicCost)
|
||||
successors, cost, heuristicCost, edgeTo := sf.successors, sf.cost, sf.heuristicCost, sf.edgeTo
|
||||
|
||||
closedSet := make(map[int]internalNode)
|
||||
openSet := &aStarPriorityQueue{nodes: make([]internalNode, 0), indexList: make(map[int]int)}
|
||||
heap.Init(openSet)
|
||||
node := internalNode{start, 0, heuristicCost(start, goal)}
|
||||
heap.Push(openSet, node)
|
||||
predecessor := make(map[int]gr.Node)
|
||||
predecessor := make(map[int]graph.Node)
|
||||
|
||||
for openSet.Len() != 0 {
|
||||
curr := heap.Pop(openSet).(internalNode)
|
||||
@@ -67,7 +67,7 @@ func AStar(start, goal gr.Node, graph gr.Graph, cost, heuristicCost gr.CostFunc)
|
||||
continue
|
||||
}
|
||||
|
||||
g := curr.gscore + cost(curr.Node, neighbor)
|
||||
g := curr.gscore + cost(edgeTo(curr.Node, neighbor))
|
||||
|
||||
if existing, exists := openSet.Find(neighbor.ID()); !exists {
|
||||
predecessor[neighbor.ID()] = curr
|
||||
@@ -87,8 +87,8 @@ func AStar(start, goal gr.Node, graph gr.Graph, cost, heuristicCost gr.CostFunc)
|
||||
//
|
||||
// BreadthFirstSearch returns the path found and the number of nodes visited in the search.
|
||||
// The returned path is nil if no path exists.
|
||||
func BreadthFirstSearch(start, goal gr.Node, graph gr.Graph) ([]gr.Node, int) {
|
||||
path, _, visited := AStar(start, goal, graph, UniformCost, NullHeuristic)
|
||||
func BreadthFirstSearch(start, goal graph.Node, gr graph.Graph) ([]graph.Node, int) {
|
||||
path, _, visited := AStar(start, goal, gr, UniformCost, NullHeuristic)
|
||||
return path, visited
|
||||
}
|
||||
|
||||
@@ -104,17 +104,17 @@ func BreadthFirstSearch(start, goal gr.Node, graph gr.Graph) ([]gr.Node, int) {
|
||||
//
|
||||
// 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 gr.CostFunc) (paths map[int][]gr.Node, costs map[int]float64) {
|
||||
func Dijkstra(source graph.Node, gr graph.Graph, cost graph.CostFunc) (paths map[int][]graph.Node, costs map[int]float64) {
|
||||
|
||||
sf := setupFuncs(graph, cost, nil)
|
||||
successors, cost := sf.successors, sf.cost
|
||||
sf := setupFuncs(gr, cost, nil)
|
||||
successors, cost, edgeTo := sf.successors, sf.cost, sf.edgeTo
|
||||
|
||||
nodes := graph.NodeList()
|
||||
nodes := gr.NodeList()
|
||||
openSet := &aStarPriorityQueue{nodes: make([]internalNode, 0), indexList: make(map[int]int)}
|
||||
closedSet := set.NewSet() // This is to make use of that same
|
||||
costs = make(map[int]float64, len(nodes)) // May overallocate, will change if it becomes a problem
|
||||
predecessor := make(map[int]gr.Node, len(nodes))
|
||||
nodeIDMap := make(map[int]gr.Node, len(nodes))
|
||||
predecessor := make(map[int]graph.Node, len(nodes))
|
||||
nodeIDMap := make(map[int]graph.Node, len(nodes))
|
||||
heap.Init(openSet)
|
||||
|
||||
costs[source.ID()] = 0
|
||||
@@ -128,7 +128,7 @@ func Dijkstra(source gr.Node, graph gr.Graph, cost gr.CostFunc) (paths map[int][
|
||||
closedSet.Add(node.ID())
|
||||
|
||||
for _, neighbor := range successors(node) {
|
||||
tmpCost := costs[node.ID()] + cost(node, neighbor)
|
||||
tmpCost := costs[node.ID()] + cost(edgeTo(node, neighbor))
|
||||
if cost, ok := costs[neighbor.ID()]; !ok {
|
||||
costs[neighbor.ID()] = tmpCost
|
||||
predecessor[neighbor.ID()] = node
|
||||
@@ -141,7 +141,7 @@ func Dijkstra(source gr.Node, graph gr.Graph, cost gr.CostFunc) (paths map[int][
|
||||
}
|
||||
}
|
||||
|
||||
paths = make(map[int][]gr.Node, len(costs))
|
||||
paths = make(map[int][]graph.Node, len(costs))
|
||||
for node, _ := range costs { // Only reconstruct the path if one exists
|
||||
paths[node] = rebuildPath(predecessor, nodeIDMap[node])
|
||||
}
|
||||
@@ -162,23 +162,23 @@ func Dijkstra(source gr.Node, graph gr.Graph, cost gr.CostFunc) (paths map[int][
|
||||
// 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.
|
||||
func BellmanFord(source gr.Node, graph gr.Graph, cost gr.CostFunc) (paths map[int][]gr.Node, costs map[int]float64, err error) {
|
||||
sf := setupFuncs(graph, cost, nil)
|
||||
successors, cost := sf.successors, sf.cost
|
||||
func BellmanFord(source graph.Node, gr graph.Graph, cost graph.CostFunc) (paths map[int][]graph.Node, costs map[int]float64, err error) {
|
||||
sf := setupFuncs(gr, cost, nil)
|
||||
successors, cost, edgeTo := sf.successors, sf.cost, sf.edgeTo
|
||||
|
||||
predecessor := make(map[int]gr.Node)
|
||||
predecessor := make(map[int]graph.Node)
|
||||
costs = make(map[int]float64)
|
||||
nodeIDMap := make(map[int]gr.Node)
|
||||
nodeIDMap := make(map[int]graph.Node)
|
||||
nodeIDMap[source.ID()] = source
|
||||
costs[source.ID()] = 0
|
||||
nodes := graph.NodeList()
|
||||
nodes := gr.NodeList()
|
||||
|
||||
for i := 1; i < len(nodes)-1; i++ {
|
||||
for _, node := range nodes {
|
||||
nodeIDMap[node.ID()] = node
|
||||
succs := successors(node)
|
||||
for _, succ := range succs {
|
||||
weight := cost(node, succ)
|
||||
weight := cost(edgeTo(node, succ))
|
||||
nodeIDMap[succ.ID()] = succ
|
||||
|
||||
if dist := costs[node.ID()] + weight; dist < costs[succ.ID()] {
|
||||
@@ -192,14 +192,14 @@ func BellmanFord(source gr.Node, graph gr.Graph, cost gr.CostFunc) (paths map[in
|
||||
|
||||
for _, node := range nodes {
|
||||
for _, succ := range successors(node) {
|
||||
weight := cost(node, succ)
|
||||
weight := cost(edgeTo(node, succ))
|
||||
if costs[node.ID()]+weight < costs[succ.ID()] {
|
||||
return nil, nil, errors.New("Negative edge cycle detected")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
paths = make(map[int][]gr.Node, len(costs))
|
||||
paths = make(map[int][]graph.Node, len(costs))
|
||||
for node, _ := range costs {
|
||||
paths[node] = rebuildPath(predecessor, nodeIDMap[node])
|
||||
}
|
||||
@@ -223,31 +223,26 @@ func BellmanFord(source gr.Node, graph gr.Graph, cost gr.CostFunc) (paths map[in
|
||||
// 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).
|
||||
func Johnson(graph gr.Graph, cost gr.CostFunc) (nodePaths map[int]map[int][]gr.Node, nodeCosts map[int]map[int]float64, err error) {
|
||||
sf := setupFuncs(graph, cost, nil)
|
||||
successors, cost := sf.successors, sf.cost
|
||||
func Johnson(gr graph.Graph, cost graph.CostFunc) (nodePaths map[int]map[int][]graph.Node, nodeCosts map[int]map[int]float64, err error) {
|
||||
sf := setupFuncs(gr, cost, nil)
|
||||
successors, cost, edgeTo := sf.successors, sf.cost, sf.edgeTo
|
||||
|
||||
/* Copy graph into a mutable one since it has to be altered for this algorithm */
|
||||
dummyGraph := concrete.NewGonumGraph(true)
|
||||
for _, node := range graph.NodeList() {
|
||||
dummyGraph := concrete.NewGraph()
|
||||
for _, node := range gr.NodeList() {
|
||||
neighbors := successors(node)
|
||||
if !dummyGraph.NodeExists(node) {
|
||||
dummyGraph.AddNode(node, neighbors)
|
||||
for _, neighbor := range neighbors {
|
||||
dummyGraph.SetEdgeCost(concrete.GonumEdge{node, neighbor}, cost(node, neighbor))
|
||||
}
|
||||
} else {
|
||||
for _, neighbor := range neighbors {
|
||||
dummyGraph.AddEdge(concrete.GonumEdge{node, neighbor})
|
||||
dummyGraph.SetEdgeCost(concrete.GonumEdge{node, neighbor}, cost(node, neighbor))
|
||||
}
|
||||
dummyGraph.NodeExists(node)
|
||||
dummyGraph.AddNode(node)
|
||||
for _, neighbor := range neighbors {
|
||||
e := edgeTo(node, neighbor)
|
||||
dummyGraph.AddEdge(e, cost(e), true)
|
||||
}
|
||||
}
|
||||
|
||||
/* Step 1: Dummy node with 0 cost edge weights to every other node*/
|
||||
dummyNode := dummyGraph.NewNode(graph.NodeList())
|
||||
for _, node := range graph.NodeList() {
|
||||
dummyGraph.SetEdgeCost(concrete.GonumEdge{dummyNode, node}, 0)
|
||||
dummyNode := dummyGraph.NewNode()
|
||||
for _, node := range gr.NodeList() {
|
||||
dummyGraph.AddEdge(concrete.Edge{dummyNode, node}, 0.0, true)
|
||||
}
|
||||
|
||||
/* Step 2: Run Bellman-Ford starting at the dummy node, abort if it detects a cycle */
|
||||
@@ -257,19 +252,20 @@ func Johnson(graph gr.Graph, cost gr.CostFunc) (nodePaths map[int]map[int][]gr.N
|
||||
}
|
||||
|
||||
/* Step 3: reweight the graph and remove the dummy node */
|
||||
for _, node := range graph.NodeList() {
|
||||
for _, node := range gr.NodeList() {
|
||||
for _, succ := range successors(node) {
|
||||
dummyGraph.SetEdgeCost(concrete.GonumEdge{node, succ}, cost(node, succ)+costs[node.ID()]-costs[succ.ID()])
|
||||
e := edgeTo(node, succ)
|
||||
dummyGraph.AddEdge(e, cost(e)+costs[node.ID()]-costs[succ.ID()], true)
|
||||
}
|
||||
}
|
||||
|
||||
dummyGraph.RemoveNode(dummyNode)
|
||||
|
||||
/* Step 4: Run Dijkstra's starting at every node */
|
||||
nodePaths = make(map[int]map[int][]gr.Node, len(graph.NodeList()))
|
||||
nodePaths = make(map[int]map[int][]graph.Node, len(gr.NodeList()))
|
||||
nodeCosts = make(map[int]map[int]float64)
|
||||
|
||||
for _, node := range graph.NodeList() {
|
||||
for _, node := range gr.NodeList() {
|
||||
nodePaths[node.ID()], nodeCosts[node.ID()] = Dijkstra(node, dummyGraph, nil)
|
||||
}
|
||||
|
||||
@@ -279,18 +275,18 @@ func Johnson(graph gr.Graph, cost gr.CostFunc) (nodePaths map[int]map[int][]gr.N
|
||||
// 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.)
|
||||
func DepthFirstSearch(start, goal gr.Node, graph gr.Graph) []gr.Node {
|
||||
sf := setupFuncs(graph, nil, nil)
|
||||
func DepthFirstSearch(start, goal graph.Node, gr graph.Graph) []graph.Node {
|
||||
sf := setupFuncs(gr, nil, nil)
|
||||
successors := sf.successors
|
||||
|
||||
closedSet := set.NewSet()
|
||||
openSet := xifo.GonumStack([]interface{}{start})
|
||||
predecessor := make(map[int]gr.Node)
|
||||
predecessor := make(map[int]graph.Node)
|
||||
|
||||
for !openSet.IsEmpty() {
|
||||
c := openSet.Pop()
|
||||
|
||||
curr := c.(gr.Node)
|
||||
curr := c.(graph.Node)
|
||||
|
||||
if closedSet.Contains(curr.ID()) {
|
||||
continue
|
||||
@@ -316,35 +312,34 @@ func DepthFirstSearch(start, goal gr.Node, graph gr.Graph) []gr.Node {
|
||||
}
|
||||
|
||||
// An admissible, consistent heuristic that won't speed up computation time at all.
|
||||
func NullHeuristic(a, b gr.Node) float64 {
|
||||
func NullHeuristic(node1, node2 graph.Node) float64 {
|
||||
return 0.0
|
||||
}
|
||||
|
||||
// Assumes all edges in the graph have the same weight (including edges that don't exist!)
|
||||
func UniformCost(a, b gr.Node) float64 {
|
||||
func UniformCost(e graph.Edge) float64 {
|
||||
if e == nil {
|
||||
return math.Inf(1)
|
||||
}
|
||||
|
||||
return 1.0
|
||||
}
|
||||
|
||||
/* Simple operations */
|
||||
|
||||
// Copies a graph into the destination; maintaining all node IDs.
|
||||
func CopyGraph(dst gr.MutableGraph, src gr.Graph) {
|
||||
func CopyGraph(dst graph.MutableGraph, src graph.Graph) {
|
||||
dst.EmptyGraph()
|
||||
dst.SetDirected(false)
|
||||
|
||||
sf := setupFuncs(src, nil, nil)
|
||||
successors, cost := sf.successors, sf.cost
|
||||
successors, cost, edgeTo := sf.successors, sf.cost, sf.edgeTo
|
||||
|
||||
for _, node := range src.NodeList() {
|
||||
succs := successors(node)
|
||||
if !dst.NodeExists(node) {
|
||||
dst.AddNode(node, succs)
|
||||
} else {
|
||||
for _, succ := range succs {
|
||||
edge := concrete.GonumEdge{node, succ}
|
||||
dst.AddEdge(edge)
|
||||
dst.SetEdgeCost(edge, cost(node, succ))
|
||||
}
|
||||
dst.AddNode(node)
|
||||
for _, succ := range succs {
|
||||
edge := edgeTo(node, succ)
|
||||
dst.AddEdge(edge, cost(edge), true)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -365,21 +360,21 @@ func CopyGraph(dst gr.MutableGraph, src gr.Graph) {
|
||||
// An undirected graph should end up with as many SCCs as there are "islands" (or subgraphs) of
|
||||
// connections, meaning having more than one strongly connected component implies that your graph
|
||||
// is not fully connected.
|
||||
func Tarjan(graph gr.Graph) (sccs [][]gr.Node) {
|
||||
func Tarjan(gr graph.Graph) (sccs [][]graph.Node) {
|
||||
index := 0
|
||||
vStack := &xifo.GonumStack{}
|
||||
stackSet := set.NewSet()
|
||||
sccs = make([][]gr.Node, 0)
|
||||
sccs = make([][]graph.Node, 0)
|
||||
|
||||
nodes := graph.NodeList()
|
||||
nodes := gr.NodeList()
|
||||
lowlinks := make(map[int]int, len(nodes))
|
||||
indices := make(map[int]int, len(nodes))
|
||||
|
||||
successors := setupFuncs(graph, nil, nil).successors
|
||||
successors := setupFuncs(gr, nil, nil).successors
|
||||
|
||||
var strongconnect func(gr.Node) []gr.Node
|
||||
var strongconnect func(graph.Node) []graph.Node
|
||||
|
||||
strongconnect = func(node gr.Node) []gr.Node {
|
||||
strongconnect = func(node graph.Node) []graph.Node {
|
||||
indices[node.ID()] = index
|
||||
lowlinks[node.ID()] = index
|
||||
index += 1
|
||||
@@ -397,12 +392,12 @@ func Tarjan(graph gr.Graph) (sccs [][]gr.Node) {
|
||||
}
|
||||
|
||||
if lowlinks[node.ID()] == indices[node.ID()] {
|
||||
scc := make([]gr.Node, 0)
|
||||
scc := make([]graph.Node, 0)
|
||||
for {
|
||||
v := vStack.Pop()
|
||||
stackSet.Remove(v.(gr.Node).ID())
|
||||
scc = append(scc, v.(gr.Node))
|
||||
if v.(gr.Node).ID() == node.ID() {
|
||||
stackSet.Remove(v.(graph.Node).ID())
|
||||
scc = append(scc, v.(graph.Node))
|
||||
if v.(graph.Node).ID() == node.ID() {
|
||||
return scc
|
||||
}
|
||||
}
|
||||
@@ -429,13 +424,13 @@ func Tarjan(graph gr.Graph) (sccs [][]gr.Node) {
|
||||
// (only one node) but only if the node listed in path exists within the graph.
|
||||
//
|
||||
// Graph must be non-nil.
|
||||
func IsPath(path []gr.Node, graph gr.Graph) bool {
|
||||
isSuccessor := setupFuncs(graph, nil, nil).isSuccessor
|
||||
func IsPath(path []graph.Node, gr graph.Graph) bool {
|
||||
isSuccessor := setupFuncs(gr, nil, nil).isSuccessor
|
||||
|
||||
if path == nil || len(path) == 0 {
|
||||
return true
|
||||
} else if len(path) == 1 {
|
||||
return graph.NodeExists(path[0])
|
||||
return gr.NodeExists(path[0])
|
||||
}
|
||||
|
||||
for i := 0; i < len(path)-1; i++ {
|
||||
@@ -454,46 +449,39 @@ puts the resulting minimum spanning tree in the dst graph */
|
||||
//
|
||||
// As with other algorithms that use Cost, the order of precedence is
|
||||
// Argument > Interface > UniformCost.
|
||||
func Prim(dst gr.MutableGraph, graph gr.EdgeListGraph, cost gr.CostFunc) {
|
||||
cost = setupFuncs(graph, cost, nil).cost
|
||||
func Prim(dst graph.MutableGraph, gr graph.EdgeListGraph, cost graph.CostFunc) {
|
||||
sf := setupFuncs(gr, cost, nil)
|
||||
cost = sf.cost
|
||||
|
||||
dst.EmptyGraph()
|
||||
dst.SetDirected(false)
|
||||
|
||||
nlist := graph.NodeList()
|
||||
nlist := gr.NodeList()
|
||||
|
||||
if nlist == nil || len(nlist) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
dst.AddNode(nlist[0], nil)
|
||||
dst.AddNode(nlist[0])
|
||||
remainingNodes := set.NewSet()
|
||||
for _, node := range nlist[1:] {
|
||||
remainingNodes.Add(node.ID())
|
||||
}
|
||||
|
||||
edgeList := graph.EdgeList()
|
||||
edgeList := gr.EdgeList()
|
||||
for remainingNodes.Cardinality() != 0 {
|
||||
edgeWeights := make(edgeSorter, 0)
|
||||
for _, edge := range edgeList {
|
||||
if dst.NodeExists(edge.Head()) && remainingNodes.Contains(edge.Tail().ID()) {
|
||||
edgeWeights = append(edgeWeights, WeightedEdge{Edge: edge, Weight: cost(edge.Head(), edge.Tail())})
|
||||
} else if dst.NodeExists(edge.Tail()) && remainingNodes.Contains(edge.Head().ID()) {
|
||||
edgeWeights = append(edgeWeights, WeightedEdge{Edge: edge, Weight: cost(edge.Tail(), edge.Head())})
|
||||
if (dst.NodeExists(edge.Head()) && remainingNodes.Contains(edge.Tail().ID())) ||
|
||||
(dst.NodeExists(edge.Tail()) && remainingNodes.Contains(edge.Head().ID())) {
|
||||
|
||||
edgeWeights = append(edgeWeights, concrete.WeightedEdge{Edge: edge, Cost: cost(edge)})
|
||||
}
|
||||
}
|
||||
|
||||
sort.Sort(edgeWeights)
|
||||
myEdge := edgeWeights[0]
|
||||
|
||||
// Since it's undirected this doesn't need to check head vs tail
|
||||
if !dst.NodeExists(myEdge.Head()) {
|
||||
dst.AddNode(myEdge.Head(), []gr.Node{myEdge.Tail()})
|
||||
} else {
|
||||
dst.AddEdge(myEdge.Edge)
|
||||
}
|
||||
dst.SetEdgeCost(myEdge.Edge, myEdge.Weight)
|
||||
|
||||
dst.AddEdge(myEdge.Edge, myEdge.Cost, false)
|
||||
remainingNodes.Remove(myEdge.Edge.Head())
|
||||
}
|
||||
|
||||
@@ -502,22 +490,20 @@ func Prim(dst gr.MutableGraph, graph gr.EdgeListGraph, cost gr.CostFunc) {
|
||||
// Generates a minimum spanning tree for a graph using discrete.DisjointSet.
|
||||
//
|
||||
// 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) {
|
||||
cost = setupFuncs(graph, cost, nil).cost
|
||||
|
||||
func Kruskal(dst graph.MutableGraph, gr graph.EdgeListGraph, cost graph.CostFunc) {
|
||||
cost = setupFuncs(gr, cost, nil).cost
|
||||
dst.EmptyGraph()
|
||||
dst.SetDirected(false)
|
||||
|
||||
edgeList := graph.EdgeList()
|
||||
edgeList := gr.EdgeList()
|
||||
edgeWeights := make(edgeSorter, 0, len(edgeList))
|
||||
for _, edge := range edgeList {
|
||||
edgeWeights = append(edgeWeights, WeightedEdge{Edge: edge, Weight: cost(edge.Head(), edge.Tail())})
|
||||
edgeWeights = append(edgeWeights, concrete.WeightedEdge{Edge: edge, Cost: cost(edge)})
|
||||
}
|
||||
|
||||
sort.Sort(edgeWeights)
|
||||
|
||||
ds := set.NewDisjointSet()
|
||||
for _, node := range graph.NodeList() {
|
||||
for _, node := range gr.NodeList() {
|
||||
ds.MakeSet(node.ID())
|
||||
}
|
||||
|
||||
@@ -526,12 +512,7 @@ func Kruskal(dst gr.MutableGraph, graph gr.EdgeListGraph, cost func(gr.Node, gr.
|
||||
// should work fine without checking both ways
|
||||
if s1, s2 := ds.Find(edge.Edge.Head().ID()), ds.Find(edge.Edge.Tail().ID); s1 != s2 {
|
||||
ds.Union(s1, s2)
|
||||
if !dst.NodeExists(edge.Edge.Head()) {
|
||||
dst.AddNode(edge.Edge.Head(), []gr.Node{edge.Edge.Tail()})
|
||||
} else {
|
||||
dst.AddEdge(edge.Edge)
|
||||
}
|
||||
dst.SetEdgeCost(edge.Edge, edge.Weight)
|
||||
dst.AddEdge(edge.Edge, edge.Cost, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -544,15 +525,15 @@ func Kruskal(dst gr.MutableGraph, graph gr.EdgeListGraph, cost func(gr.Node, gr.
|
||||
// immediate dominators etc.
|
||||
//
|
||||
// The int map[int]*set.Set is the node's ID.
|
||||
func Dominators(start gr.Node, graph gr.Graph) map[int]*set.Set {
|
||||
func Dominators(start graph.Node, gr graph.Graph) map[int]*set.Set {
|
||||
allNodes := set.NewSet()
|
||||
nlist := graph.NodeList()
|
||||
nlist := gr.NodeList()
|
||||
dominators := make(map[int]*set.Set, len(nlist))
|
||||
for _, node := range nlist {
|
||||
allNodes.Add(node.ID())
|
||||
}
|
||||
|
||||
predecessors := setupFuncs(graph, nil, nil).predecessors
|
||||
predecessors := setupFuncs(gr, nil, nil).predecessors
|
||||
|
||||
for _, node := range nlist {
|
||||
dominators[node.ID()] = set.NewSet()
|
||||
@@ -596,11 +577,11 @@ 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.
|
||||
func PostDominators(end gr.Node, graph gr.Graph) map[int]*set.Set {
|
||||
successors := setupFuncs(graph, nil, nil).successors
|
||||
func PostDominators(end graph.Node, gr graph.Graph) map[int]*set.Set {
|
||||
successors := setupFuncs(gr, nil, nil).successors
|
||||
|
||||
allNodes := set.NewSet()
|
||||
nlist := graph.NodeList()
|
||||
nlist := gr.NodeList()
|
||||
dominators := make(map[int]*set.Set, len(nlist))
|
||||
for _, node := range nlist {
|
||||
allNodes.Add(node.ID())
|
||||
|
@@ -3,43 +3,69 @@ package search
|
||||
import (
|
||||
"container/heap"
|
||||
|
||||
gr "github.com/gonum/graph"
|
||||
"github.com/gonum/graph"
|
||||
"github.com/gonum/graph/concrete"
|
||||
)
|
||||
|
||||
type searchFuncs struct {
|
||||
successors, predecessors, neighbors func(gr.Node) []gr.Node
|
||||
isSuccessor, isPredecessor, isNeighbor func(gr.Node, gr.Node) bool
|
||||
cost, heuristicCost gr.CostFunc
|
||||
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(graph gr.Graph, cost, heuristicCost gr.CostFunc) searchFuncs {
|
||||
func setupFuncs(gr graph.Graph, cost graph.CostFunc, heuristicCost graph.HeuristicCostFunc) searchFuncs {
|
||||
|
||||
sf := searchFuncs{}
|
||||
|
||||
switch g := graph.(type) {
|
||||
case gr.DirectedGraph:
|
||||
switch g := gr.(type) {
|
||||
case graph.DirectedGraph:
|
||||
sf.successors = g.Successors
|
||||
sf.predecessors = g.Predecessors
|
||||
sf.neighbors = g.Neighbors
|
||||
sf.isSuccessor = g.IsSuccessor
|
||||
sf.isPredecessor = g.IsPredecessor
|
||||
sf.isNeighbor = g.IsNeighbor
|
||||
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
|
||||
sf.isSuccessor = g.IsNeighbor
|
||||
sf.isPredecessor = g.IsNeighbor
|
||||
sf.isNeighbor = g.IsNeighbor
|
||||
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 := graph.(gr.HeuristicCoster); ok {
|
||||
if g, ok := gr.(graph.HeuristicCoster); ok {
|
||||
sf.heuristicCost = g.HeuristicCost
|
||||
} else {
|
||||
sf.heuristicCost = NullHeuristic
|
||||
@@ -49,7 +75,7 @@ func setupFuncs(graph gr.Graph, cost, heuristicCost gr.CostFunc) searchFuncs {
|
||||
if cost != nil {
|
||||
sf.cost = cost
|
||||
} else {
|
||||
if g, ok := graph.(gr.Coster); ok {
|
||||
if g, ok := gr.(graph.Coster); ok {
|
||||
sf.cost = g.Cost
|
||||
} else {
|
||||
sf.cost = UniformCost
|
||||
@@ -59,25 +85,16 @@ func setupFuncs(graph gr.Graph, cost, heuristicCost gr.CostFunc) searchFuncs {
|
||||
return sf
|
||||
}
|
||||
|
||||
/* Purely internal data structures and functions (mostly for sorting) */
|
||||
|
||||
// A package that contains an edge (as from EdgeList), and a Weight (as if Cost(Edge.Head(),
|
||||
// Edge.Tail()) had been called.)
|
||||
type WeightedEdge struct {
|
||||
gr.Edge
|
||||
Weight float64
|
||||
}
|
||||
|
||||
/** Sorts a list of edges by weight, agnostic to repeated edges as well as direction **/
|
||||
|
||||
type edgeSorter []WeightedEdge
|
||||
type edgeSorter []concrete.WeightedEdge
|
||||
|
||||
func (el edgeSorter) Len() int {
|
||||
return len(el)
|
||||
}
|
||||
|
||||
func (el edgeSorter) Less(i, j int) bool {
|
||||
return el[i].Weight < el[j].Weight
|
||||
return el[i].Cost < el[j].Cost
|
||||
}
|
||||
|
||||
func (el edgeSorter) Swap(i, j int) {
|
||||
@@ -87,7 +104,7 @@ func (el edgeSorter) Swap(i, j int) {
|
||||
/** Keeps track of a node's scores so they can be used in a priority queue for A* **/
|
||||
|
||||
type internalNode struct {
|
||||
gr.Node
|
||||
graph.Node
|
||||
gscore, fscore float64
|
||||
}
|
||||
|
||||
@@ -151,7 +168,7 @@ func (pq *aStarPriorityQueue) Exists(id int) bool {
|
||||
return ok
|
||||
}
|
||||
|
||||
type denseNodeSorter []gr.Node
|
||||
type denseNodeSorter []graph.Node
|
||||
|
||||
func (dns denseNodeSorter) Less(i, j int) bool {
|
||||
return dns[i].ID() < dns[j].ID()
|
||||
@@ -168,11 +185,11 @@ func (dns denseNodeSorter) Len() int {
|
||||
// General utility funcs
|
||||
|
||||
// Rebuilds a path backwards from the goal.
|
||||
func rebuildPath(predecessors map[int]gr.Node, goal gr.Node) []gr.Node {
|
||||
func rebuildPath(predecessors map[int]graph.Node, goal graph.Node) []graph.Node {
|
||||
if n, ok := goal.(internalNode); ok {
|
||||
goal = n.Node
|
||||
}
|
||||
path := []gr.Node{goal}
|
||||
path := []graph.Node{goal}
|
||||
curr := goal
|
||||
for prev, ok := predecessors[curr.ID()]; ok; prev, ok = predecessors[curr.ID()] {
|
||||
if n, ok := prev.(internalNode); ok {
|
||||
|
@@ -16,7 +16,7 @@ func TestSimpleAStar(t *testing.T) {
|
||||
t.Fatal("Couldn't generate tilegraph")
|
||||
}
|
||||
|
||||
path, cost, _ := search.AStar(concrete.GonumNode(1), concrete.GonumNode(14), tg, nil, nil)
|
||||
path, cost, _ := search.AStar(concrete.Node(1), concrete.Node(14), tg, nil, nil)
|
||||
if math.Abs(cost-4.0) > .00001 {
|
||||
t.Errorf("A* reports incorrect cost for simple tilegraph search")
|
||||
}
|
||||
@@ -39,14 +39,14 @@ func TestSimpleAStar(t *testing.T) {
|
||||
func TestBiggerAStar(t *testing.T) {
|
||||
tg := concrete.NewTileGraph(3, 3, true)
|
||||
|
||||
path, cost, _ := search.AStar(concrete.GonumNode(0), concrete.GonumNode(8), tg, nil, nil)
|
||||
path, cost, _ := search.AStar(concrete.Node(0), concrete.Node(8), tg, nil, nil)
|
||||
|
||||
if math.Abs(cost-4.0) > .00001 || !search.IsPath(path, tg) {
|
||||
t.Error("Non-optimal or impossible path found for 3x3 grid")
|
||||
}
|
||||
|
||||
tg = concrete.NewTileGraph(1000, 1000, true)
|
||||
path, cost, _ = search.AStar(concrete.GonumNode(00), concrete.GonumNode(999*1000+999), tg, nil, nil)
|
||||
path, cost, _ = search.AStar(concrete.Node(00), concrete.Node(999*1000+999), tg, nil, nil)
|
||||
if !search.IsPath(path, tg) || cost != 1998.0 {
|
||||
t.Error("Non-optimal or impossible path found for 100x100 grid; cost:", cost, "path:\n"+tg.PathString(path))
|
||||
}
|
||||
@@ -67,7 +67,7 @@ func TestObstructedAStar(t *testing.T) {
|
||||
tg.SetPassability(4, 9, false)
|
||||
|
||||
rows, cols := tg.Dimensions()
|
||||
path, cost1, expanded := search.AStar(concrete.GonumNode(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, nil)
|
||||
|
||||
if !search.IsPath(path, tg) {
|
||||
t.Error("Path doesn't exist in obstructed graph")
|
||||
@@ -81,7 +81,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.GonumNode(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, nil, ManhattanHeuristic)
|
||||
if !search.IsPath(path, tg) {
|
||||
t.Error("Path doesn't exist when using heuristic on obstructed graph")
|
||||
}
|
||||
@@ -126,7 +126,7 @@ func TestSmallAStar(t *testing.T) {
|
||||
// assert that AStar finds each path
|
||||
for goalID, dPath := range dPaths {
|
||||
exp := fmt.Sprintln(dPath, dCosts[goalID])
|
||||
aPath, aCost, _ := search.AStar(start, concrete.GonumNode(goalID), gg, nil, heur)
|
||||
aPath, aCost, _ := search.AStar(start, concrete.Node(goalID), gg, nil, heur)
|
||||
got := fmt.Sprintln(aPath, aCost)
|
||||
if got != exp {
|
||||
t.Error("expected", exp, "got", got)
|
||||
@@ -136,10 +136,11 @@ func TestSmallAStar(t *testing.T) {
|
||||
}
|
||||
|
||||
func ExampleBreadthFirstSearch() {
|
||||
g := concrete.NewGonumGraph(true)
|
||||
var n0, n1, n2, n3 concrete.GonumNode = 0, 1, 2, 3
|
||||
g.AddNode(n0, []graph.Node{n1, n2})
|
||||
g.AddEdge(concrete.GonumEdge{n2, n3})
|
||||
g := concrete.NewGraph()
|
||||
var n0, n1, n2, n3 concrete.Node = 0, 1, 2, 3
|
||||
g.AddEdge(concrete.Edge{n0, n1}, 1.0, true)
|
||||
g.AddEdge(concrete.Edge{n0, n2}, 1.0, true)
|
||||
g.AddEdge(concrete.Edge{n2, n3}, 1.0, true)
|
||||
path, v := search.BreadthFirstSearch(n0, n3, g)
|
||||
fmt.Println("path:", path)
|
||||
fmt.Println("nodes visited:", v)
|
||||
@@ -148,7 +149,7 @@ func ExampleBreadthFirstSearch() {
|
||||
// nodes visited: 4
|
||||
}
|
||||
|
||||
func newSmallGonumGraph() *concrete.GonumGraph {
|
||||
func newSmallGonumGraph() *concrete.Graph {
|
||||
eds := []struct{ n1, n2, edgeCost int }{
|
||||
{1, 2, 7},
|
||||
{1, 3, 9},
|
||||
@@ -160,17 +161,16 @@ func newSmallGonumGraph() *concrete.GonumGraph {
|
||||
{4, 5, 7},
|
||||
{5, 6, 9},
|
||||
}
|
||||
g := concrete.NewGonumGraph(false)
|
||||
for n := concrete.GonumNode(1); n <= 6; n++ {
|
||||
g.AddNode(n, nil)
|
||||
g := concrete.NewGraph()
|
||||
for n := concrete.Node(1); n <= 6; n++ {
|
||||
g.AddNode(n)
|
||||
}
|
||||
for _, ed := range eds {
|
||||
e := concrete.GonumEdge{
|
||||
concrete.GonumNode(ed.n1),
|
||||
concrete.GonumNode(ed.n2),
|
||||
e := concrete.Edge{
|
||||
concrete.Node(ed.n1),
|
||||
concrete.Node(ed.n2),
|
||||
}
|
||||
g.AddEdge(e)
|
||||
g.SetEdgeCost(e, float64(ed.edgeCost))
|
||||
g.AddEdge(e, float64(ed.edgeCost), false)
|
||||
}
|
||||
return g
|
||||
}
|
||||
@@ -203,7 +203,7 @@ func monotonic(g costEdgeListGraph, heur func(n1, n2 graph.Node) float64) (bool,
|
||||
for _, edge := range g.EdgeList() {
|
||||
head := edge.Head()
|
||||
tail := edge.Tail()
|
||||
if heur(head, goal) > g.Cost(head, tail)+heur(tail, goal) {
|
||||
if heur(head, goal) > g.Cost(edge)+heur(tail, goal) {
|
||||
return false, edge, goal
|
||||
}
|
||||
}
|
||||
@@ -214,7 +214,7 @@ func monotonic(g costEdgeListGraph, heur func(n1, n2 graph.Node) float64) (bool,
|
||||
// Test for correct result on a small graph easily solvable by hand
|
||||
func TestDijkstraSmall(t *testing.T) {
|
||||
g := newSmallGonumGraph()
|
||||
paths, lens := search.Dijkstra(concrete.GonumNode(1), g, nil)
|
||||
paths, lens := search.Dijkstra(concrete.Node(1), g, nil)
|
||||
s := fmt.Sprintln(len(paths), len(lens))
|
||||
for i := 1; i <= 6; i++ {
|
||||
s += fmt.Sprintln(paths[i], lens[i])
|
||||
@@ -232,39 +232,40 @@ func TestDijkstraSmall(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestIsPath(t *testing.T) {
|
||||
g := concrete.NewGonumGraph(true)
|
||||
if search.IsPath(nil, g) != true {
|
||||
t.Error("nil path")
|
||||
g := concrete.NewGraph()
|
||||
if !search.IsPath(nil, g) {
|
||||
t.Error("IsPath returns false on nil path")
|
||||
}
|
||||
p := []graph.Node{concrete.GonumNode(0)}
|
||||
if search.IsPath(p, g) != false {
|
||||
t.Error("node not in graph")
|
||||
p := []graph.Node{concrete.Node(0)}
|
||||
if search.IsPath(p, g) {
|
||||
t.Error("IsPath returns true on nonexistant node")
|
||||
}
|
||||
g.AddNode(p[0], nil)
|
||||
if search.IsPath(p, g) != true {
|
||||
t.Error("single node in graph")
|
||||
g.AddNode(p[0])
|
||||
if !search.IsPath(p, g) {
|
||||
t.Error("IsPath returns false on single-length path with existing node")
|
||||
}
|
||||
p = append(p, concrete.GonumNode(1))
|
||||
g.AddNode(p[1], nil)
|
||||
if search.IsPath(p, g) != false {
|
||||
t.Error("unconnected nodes")
|
||||
p = append(p, concrete.Node(1))
|
||||
g.AddNode(p[1])
|
||||
if search.IsPath(p, g) {
|
||||
t.Error("IsPath returns true on bad path of length 2")
|
||||
}
|
||||
g.AddEdge(concrete.GonumEdge{p[0], p[1]})
|
||||
if search.IsPath(p, g) != true {
|
||||
t.Error("connected nodes")
|
||||
g.AddEdge(concrete.Edge{p[0], p[1]}, 1.0, true)
|
||||
if !search.IsPath(p, g) {
|
||||
t.Error("IsPath returns false on correct path of length 2")
|
||||
}
|
||||
p[0], p[1] = p[1], p[0]
|
||||
if search.IsPath(p, g) != false {
|
||||
t.Error("reverse path")
|
||||
if search.IsPath(p, g) {
|
||||
t.Error("IsPath erroneously returns true for a reverse path")
|
||||
}
|
||||
p = []graph.Node{p[1], p[0], concrete.GonumNode(2)}
|
||||
g.AddEdge(concrete.GonumEdge{p[1], p[2]})
|
||||
if search.IsPath(p, g) != true {
|
||||
t.Error("three nodes")
|
||||
p = []graph.Node{p[1], p[0], concrete.Node(2)}
|
||||
g.AddEdge(concrete.Edge{p[1], p[2]}, 1.0, true)
|
||||
if !search.IsPath(p, g) {
|
||||
t.Error("IsPath does not find a correct path for path > 2 nodes")
|
||||
}
|
||||
g = concrete.NewGonumGraph(false)
|
||||
g.AddNode(p[1], []graph.Node{p[0], p[2]})
|
||||
if search.IsPath(p, g) != true {
|
||||
t.Error("undirected")
|
||||
g = concrete.NewGraph()
|
||||
g.AddEdge(concrete.Edge{p[1], p[0]}, 1.0, false)
|
||||
g.AddEdge(concrete.Edge{p[1], p[2]}, 1.0, false)
|
||||
if !search.IsPath(p, g) {
|
||||
t.Error("IsPath does not correctly account for undirected behavior")
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user