func TestCrunch(t *testing.T) { dg := concrete.NewDenseGraph(5, true) dg.Crunch() if len(dg.NodeList()) != 5 || len(dg.DirectedEdgeList()) != 5*5 { t.Errorf("Crunch did something") } }
func TestDenseLists(t *testing.T) { dg := concrete.NewDenseGraph(15, true) nodes := nodeSorter(dg.NodeList()) if len(nodes) != 15 { t.Fatalf("Wrong number of nodes") } sort.Sort(nodes) for i, node := range dg.NodeList() { if i != node.ID() { t.Errorf("Node list doesn't return properly id'd nodes") } } edges := dg.DirectedEdgeList() if len(edges) != 15*15 { t.Errorf("Improper number of edges for passable dense graph") } 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") } }
func TestFWOneEdge(t *testing.T) { dg := concrete.NewDenseGraph(2, true) aPaths, sPath := search.FloydWarshall(dg, nil) path, cost, err := sPath(concrete.Node(0), concrete.Node(1)) if err != nil { t.Fatal(err) } if math.Abs(cost-1) > 1e-6 { t.Errorf("FW got wrong cost %f", cost) } if len(path) != 2 || path[0].ID() != 0 && path[1].ID() != 1 { t.Errorf("Wrong path in FW %v", path) } paths, cost, err := aPaths(concrete.Node(0), concrete.Node(1)) if err != nil { t.Fatal(err) } if math.Abs(cost-1) > 1e-6 { t.Errorf("FW got wrong cost %f", cost) } if len(paths) != 1 { t.Errorf("Didn't get right paths in FW %v", paths) } path = paths[0] if len(path) != 2 || path[0].ID() != 0 && path[1].ID() != 1 { t.Errorf("Wrong path in FW allpaths %v", path) } }
func TestFWTwoPaths(t *testing.T) { dg := concrete.NewDenseGraph(5, false) // Adds two paths from 0->2 of equal length dg.SetEdgeCost(concrete.Edge{concrete.Node(0), concrete.Node(2)}, 2, true) dg.SetEdgeCost(concrete.Edge{concrete.Node(0), concrete.Node(1)}, 1, true) dg.SetEdgeCost(concrete.Edge{concrete.Node(1), concrete.Node(2)}, 1, true) aPaths, sPath := search.FloydWarshall(dg, nil) path, cost, err := sPath(concrete.Node(0), concrete.Node(2)) if err != nil { t.Fatal(err) } if math.Abs(cost-2) > .00001 { t.Errorf("Path has incorrect cost, %f", cost) } if len(path) == 2 && path[0].ID() == 0 && path[1].ID() == 2 { t.Logf("Got correct path: %v", path) } else if len(path) == 3 && path[0].ID() == 0 && path[1].ID() == 1 && path[2].ID() == 2 { t.Logf("Got correct path %v", path) } else { t.Errorf("Got wrong path %v", path) } paths, cost, err := aPaths(concrete.Node(0), concrete.Node(2)) if err != nil { t.Fatal(err) } if math.Abs(cost-2) > .00001 { t.Errorf("All paths function gets incorrect cost, %f", cost) } if len(paths) != 2 { t.Fatalf("Didn't get all shortest paths %v", paths) } for _, path := range paths { if len(path) == 2 && path[0].ID() == 0 && path[1].ID() == 2 { t.Logf("Got correct path for all paths: %v", path) } else if len(path) == 3 && path[0].ID() == 0 && path[1].ID() == 1 && path[2].ID() == 2 { t.Logf("Got correct path for all paths %v", path) } else { t.Errorf("Got wrong path for all paths %v", path) } } }
func TestBasicDensePassable(t *testing.T) { dg := concrete.NewDenseGraph(5, true) if dg == nil { t.Fatal("Directed graph could not be made") } for i := 0; i < 5; i++ { if !dg.NodeExists(concrete.Node(i)) { t.Errorf("Node that should exist doesn't: %d", i) } 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.Node(i)) { t.Errorf("Node exists that shouldn't: %d", i) } } }
func TestDenseAddRemove(t *testing.T) { dg := concrete.NewDenseGraph(10, false) dg.SetEdgeCost(concrete.Edge{concrete.Node(0), concrete.Node(2)}, 1, false) 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.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.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.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.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.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.Edge{concrete.Node(0), concrete.Node(2)}, true) 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.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.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.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.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.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.Edge{concrete.Node(0), concrete.Node(2)}, 2, true) // I figure we've torture tested Neighbors/Successors/Predecessors at this point // so we'll just use the bool functions now if dg.EdgeTo(concrete.Node(0), concrete.Node(2)) == nil { t.Error("Adding directed edge didn't change successor back") } 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.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.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") } }
// Tests with multiple right paths, but also one dead-end path // and one path that reaches the goal, but not optimally func TestFWConfoundingPath(t *testing.T) { dg := concrete.NewDenseGraph(6, false) // Add a path from 0->5 of cost 4 dg.SetEdgeCost(concrete.Edge{concrete.Node(0), concrete.Node(1)}, 1, true) dg.SetEdgeCost(concrete.Edge{concrete.Node(1), concrete.Node(2)}, 1, true) dg.SetEdgeCost(concrete.Edge{concrete.Node(2), concrete.Node(3)}, 1, true) dg.SetEdgeCost(concrete.Edge{concrete.Node(3), concrete.Node(5)}, 1, true) // Add direct edge to goal of cost 4 dg.SetEdgeCost(concrete.Edge{concrete.Node(0), concrete.Node(5)}, 4, true) // Add edge to a node that's still optimal dg.SetEdgeCost(concrete.Edge{concrete.Node(0), concrete.Node(2)}, 2, true) // Add edge to 3 that's overpriced dg.SetEdgeCost(concrete.Edge{concrete.Node(0), concrete.Node(3)}, 4, true) // Add very cheap edge to 4 which is a dead end dg.SetEdgeCost(concrete.Edge{concrete.Node(0), concrete.Node(4)}, 0.25, true) aPaths, sPath := search.FloydWarshall(dg, nil) path, cost, err := sPath(concrete.Node(0), concrete.Node(5)) if err != nil { t.Fatal(err) } if math.Abs(cost-4) > 1e-6 { t.Errorf("Incorrect cost %f", cost) } if len(path) == 5 && path[0].ID() == 0 && path[1].ID() == 1 && path[2].ID() == 2 && path[3].ID() == 3 && path[4].ID() == 5 { t.Logf("Correct path found for single path %v", path) } else if len(path) == 2 && path[0].ID() == 0 && path[1].ID() == 5 { t.Logf("Correct path found for single path %v", path) } else if len(path) == 4 && path[0].ID() == 0 && path[1].ID() == 2 && path[2].ID() == 3 && path[3].ID() == 5 { t.Logf("Correct path found for single path %v", path) } else { t.Errorf("Wrong path found for single path %v", path) } paths, cost, err := aPaths(concrete.Node(0), concrete.Node(5)) if err != nil { t.Fatal(err) } if math.Abs(cost-4) > 1e-6 { t.Errorf("Incorrect cost %f", cost) } if len(paths) != 3 { t.Errorf("Wrong paths gotten for all paths %v", paths) } for _, path := range paths { if len(path) == 5 && path[0].ID() == 0 && path[1].ID() == 1 && path[2].ID() == 2 && path[3].ID() == 3 && path[4].ID() == 5 { t.Logf("Correct path found for multi path %v", path) } else if len(path) == 2 && path[0].ID() == 0 && path[1].ID() == 5 { t.Logf("Correct path found for multi path %v", path) } else if len(path) == 4 && path[0].ID() == 0 && path[1].ID() == 2 && path[2].ID() == 3 && path[3].ID() == 5 { t.Logf("Correct path found for multi path %v", path) } else { t.Errorf("Wrong path found for multi path %v", path) } } 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.Node(4), concrete.Node(5)) if err != nil { t.Log("Success!", err) } else { t.Errorf("Path was found by FW multi-path where one shouldn't be %v", paths) } }