Files
gonum/graph/path/internal/testgraphs/shortest.go

721 lines
22 KiB
Go

// Copyright ©2014 The Gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package testgraphs
import (
"math"
"gonum.org/v1/gonum/graph"
"gonum.org/v1/gonum/graph/simple"
)
// ShortestPathTests are graphs used to test the static shortest path routines in path: BellmanFord,
// DijkstraAllPaths, DijkstraFrom, FloydWarshall and Johnson, and the static degenerate case for the
// dynamic shortest path routine in path/dynamic: DStarLite.
var ShortestPathTests = []struct {
Name string
Graph func() graph.WeightedEdgeAdder
Edges []simple.WeightedEdge
HasNegativeWeight bool
HasNegativeCycle bool
HasNegativeCycleInPath bool
Query simple.Edge
Weight float64
WantPaths [][]int64
HasUniquePath bool
NoPathFor simple.Edge
}{
// Positive weighted graphs.
{
Name: "empty directed",
Graph: func() graph.WeightedEdgeAdder { return simple.NewWeightedDirectedGraph(0, math.Inf(1)) },
Query: simple.Edge{F: simple.Node(0), T: simple.Node(1)},
Weight: math.Inf(1),
NoPathFor: simple.Edge{F: simple.Node(0), T: simple.Node(1)},
},
{
Name: "empty undirected",
Graph: func() graph.WeightedEdgeAdder { return simple.NewWeightedUndirectedGraph(0, math.Inf(1)) },
Query: simple.Edge{F: simple.Node(0), T: simple.Node(1)},
Weight: math.Inf(1),
NoPathFor: simple.Edge{F: simple.Node(0), T: simple.Node(1)},
},
{
Name: "one edge directed",
Graph: func() graph.WeightedEdgeAdder { return simple.NewWeightedDirectedGraph(0, math.Inf(1)) },
Edges: []simple.WeightedEdge{
{F: simple.Node(0), T: simple.Node(1), W: 1},
},
Query: simple.Edge{F: simple.Node(0), T: simple.Node(1)},
Weight: 1,
WantPaths: [][]int64{
{0, 1},
},
HasUniquePath: true,
NoPathFor: simple.Edge{F: simple.Node(2), T: simple.Node(3)},
},
{
Name: "one edge self directed",
Graph: func() graph.WeightedEdgeAdder { return simple.NewWeightedDirectedGraph(0, math.Inf(1)) },
Edges: []simple.WeightedEdge{
{F: simple.Node(0), T: simple.Node(1), W: 1},
},
Query: simple.Edge{F: simple.Node(0), T: simple.Node(0)},
Weight: 0,
WantPaths: [][]int64{
{0},
},
HasUniquePath: true,
NoPathFor: simple.Edge{F: simple.Node(2), T: simple.Node(3)},
},
{
Name: "one edge undirected",
Graph: func() graph.WeightedEdgeAdder { return simple.NewWeightedUndirectedGraph(0, math.Inf(1)) },
Edges: []simple.WeightedEdge{
{F: simple.Node(0), T: simple.Node(1), W: 1},
},
Query: simple.Edge{F: simple.Node(0), T: simple.Node(1)},
Weight: 1,
WantPaths: [][]int64{
{0, 1},
},
HasUniquePath: true,
NoPathFor: simple.Edge{F: simple.Node(2), T: simple.Node(3)},
},
{
Name: "two paths directed",
Graph: func() graph.WeightedEdgeAdder { return simple.NewWeightedDirectedGraph(0, math.Inf(1)) },
Edges: []simple.WeightedEdge{
{F: simple.Node(0), T: simple.Node(2), W: 2},
{F: simple.Node(0), T: simple.Node(1), W: 1},
{F: simple.Node(1), T: simple.Node(2), W: 1},
},
Query: simple.Edge{F: simple.Node(0), T: simple.Node(2)},
Weight: 2,
WantPaths: [][]int64{
{0, 1, 2},
{0, 2},
},
HasUniquePath: false,
NoPathFor: simple.Edge{F: simple.Node(2), T: simple.Node(1)},
},
{
Name: "two paths undirected",
Graph: func() graph.WeightedEdgeAdder { return simple.NewWeightedUndirectedGraph(0, math.Inf(1)) },
Edges: []simple.WeightedEdge{
{F: simple.Node(0), T: simple.Node(2), W: 2},
{F: simple.Node(0), T: simple.Node(1), W: 1},
{F: simple.Node(1), T: simple.Node(2), W: 1},
},
Query: simple.Edge{F: simple.Node(0), T: simple.Node(2)},
Weight: 2,
WantPaths: [][]int64{
{0, 1, 2},
{0, 2},
},
HasUniquePath: false,
NoPathFor: simple.Edge{F: simple.Node(2), T: simple.Node(4)},
},
{
Name: "confounding paths directed",
Graph: func() graph.WeightedEdgeAdder { return simple.NewWeightedDirectedGraph(0, math.Inf(1)) },
Edges: []simple.WeightedEdge{
// Add a path from 0->5 of weight 4
{F: simple.Node(0), T: simple.Node(1), W: 1},
{F: simple.Node(1), T: simple.Node(2), W: 1},
{F: simple.Node(2), T: simple.Node(3), W: 1},
{F: simple.Node(3), T: simple.Node(5), W: 1},
// Add direct edge to goal of weight 4
{F: simple.Node(0), T: simple.Node(5), W: 4},
// Add edge to a node that's still optimal
{F: simple.Node(0), T: simple.Node(2), W: 2},
// Add edge to 3 that's overpriced
{F: simple.Node(0), T: simple.Node(3), W: 4},
// Add very cheap edge to 4 which is a dead end
{F: simple.Node(0), T: simple.Node(4), W: 0.25},
},
Query: simple.Edge{F: simple.Node(0), T: simple.Node(5)},
Weight: 4,
WantPaths: [][]int64{
{0, 1, 2, 3, 5},
{0, 2, 3, 5},
{0, 5},
},
HasUniquePath: false,
NoPathFor: simple.Edge{F: simple.Node(4), T: simple.Node(5)},
},
{
Name: "confounding paths undirected",
Graph: func() graph.WeightedEdgeAdder { return simple.NewWeightedUndirectedGraph(0, math.Inf(1)) },
Edges: []simple.WeightedEdge{
// Add a path from 0->5 of weight 4
{F: simple.Node(0), T: simple.Node(1), W: 1},
{F: simple.Node(1), T: simple.Node(2), W: 1},
{F: simple.Node(2), T: simple.Node(3), W: 1},
{F: simple.Node(3), T: simple.Node(5), W: 1},
// Add direct edge to goal of weight 4
{F: simple.Node(0), T: simple.Node(5), W: 4},
// Add edge to a node that's still optimal
{F: simple.Node(0), T: simple.Node(2), W: 2},
// Add edge to 3 that's overpriced
{F: simple.Node(0), T: simple.Node(3), W: 4},
// Add very cheap edge to 4 which is a dead end
{F: simple.Node(0), T: simple.Node(4), W: 0.25},
},
Query: simple.Edge{F: simple.Node(0), T: simple.Node(5)},
Weight: 4,
WantPaths: [][]int64{
{0, 1, 2, 3, 5},
{0, 2, 3, 5},
{0, 5},
},
HasUniquePath: false,
NoPathFor: simple.Edge{F: simple.Node(5), T: simple.Node(6)},
},
{
Name: "confounding paths directed 2-step",
Graph: func() graph.WeightedEdgeAdder { return simple.NewWeightedDirectedGraph(0, math.Inf(1)) },
Edges: []simple.WeightedEdge{
// Add a path from 0->5 of weight 4
{F: simple.Node(0), T: simple.Node(1), W: 1},
{F: simple.Node(1), T: simple.Node(2), W: 1},
{F: simple.Node(2), T: simple.Node(3), W: 1},
{F: simple.Node(3), T: simple.Node(5), W: 1},
// Add two step path to goal of weight 4
{F: simple.Node(0), T: simple.Node(6), W: 2},
{F: simple.Node(6), T: simple.Node(5), W: 2},
// Add edge to a node that's still optimal
{F: simple.Node(0), T: simple.Node(2), W: 2},
// Add edge to 3 that's overpriced
{F: simple.Node(0), T: simple.Node(3), W: 4},
// Add very cheap edge to 4 which is a dead end
{F: simple.Node(0), T: simple.Node(4), W: 0.25},
},
Query: simple.Edge{F: simple.Node(0), T: simple.Node(5)},
Weight: 4,
WantPaths: [][]int64{
{0, 1, 2, 3, 5},
{0, 2, 3, 5},
{0, 6, 5},
},
HasUniquePath: false,
NoPathFor: simple.Edge{F: simple.Node(4), T: simple.Node(5)},
},
{
Name: "confounding paths undirected 2-step",
Graph: func() graph.WeightedEdgeAdder { return simple.NewWeightedUndirectedGraph(0, math.Inf(1)) },
Edges: []simple.WeightedEdge{
// Add a path from 0->5 of weight 4
{F: simple.Node(0), T: simple.Node(1), W: 1},
{F: simple.Node(1), T: simple.Node(2), W: 1},
{F: simple.Node(2), T: simple.Node(3), W: 1},
{F: simple.Node(3), T: simple.Node(5), W: 1},
// Add two step path to goal of weight 4
{F: simple.Node(0), T: simple.Node(6), W: 2},
{F: simple.Node(6), T: simple.Node(5), W: 2},
// Add edge to a node that's still optimal
{F: simple.Node(0), T: simple.Node(2), W: 2},
// Add edge to 3 that's overpriced
{F: simple.Node(0), T: simple.Node(3), W: 4},
// Add very cheap edge to 4 which is a dead end
{F: simple.Node(0), T: simple.Node(4), W: 0.25},
},
Query: simple.Edge{F: simple.Node(0), T: simple.Node(5)},
Weight: 4,
WantPaths: [][]int64{
{0, 1, 2, 3, 5},
{0, 2, 3, 5},
{0, 6, 5},
},
HasUniquePath: false,
NoPathFor: simple.Edge{F: simple.Node(5), T: simple.Node(7)},
},
{
Name: "zero-weight cycle directed",
Graph: func() graph.WeightedEdgeAdder { return simple.NewWeightedDirectedGraph(0, math.Inf(1)) },
Edges: []simple.WeightedEdge{
// Add a path from 0->4 of weight 4
{F: simple.Node(0), T: simple.Node(1), W: 1},
{F: simple.Node(1), T: simple.Node(2), W: 1},
{F: simple.Node(2), T: simple.Node(3), W: 1},
{F: simple.Node(3), T: simple.Node(4), W: 1},
// Add a zero-weight cycle.
{F: simple.Node(1), T: simple.Node(5), W: 0},
{F: simple.Node(5), T: simple.Node(1), W: 0},
},
Query: simple.Edge{F: simple.Node(0), T: simple.Node(4)},
Weight: 4,
WantPaths: [][]int64{
{0, 1, 2, 3, 4},
},
HasUniquePath: false,
NoPathFor: simple.Edge{F: simple.Node(4), T: simple.Node(5)},
},
{
Name: "zero-weight cycle^2 directed",
Graph: func() graph.WeightedEdgeAdder { return simple.NewWeightedDirectedGraph(0, math.Inf(1)) },
Edges: []simple.WeightedEdge{
// Add a path from 0->4 of weight 4
{F: simple.Node(0), T: simple.Node(1), W: 1},
{F: simple.Node(1), T: simple.Node(2), W: 1},
{F: simple.Node(2), T: simple.Node(3), W: 1},
{F: simple.Node(3), T: simple.Node(4), W: 1},
// Add a zero-weight cycle.
{F: simple.Node(1), T: simple.Node(5), W: 0},
{F: simple.Node(5), T: simple.Node(1), W: 0},
// With its own zero-weight cycle.
{F: simple.Node(5), T: simple.Node(6), W: 0},
{F: simple.Node(6), T: simple.Node(5), W: 0},
},
Query: simple.Edge{F: simple.Node(0), T: simple.Node(4)},
Weight: 4,
WantPaths: [][]int64{
{0, 1, 2, 3, 4},
},
HasUniquePath: false,
NoPathFor: simple.Edge{F: simple.Node(4), T: simple.Node(5)},
},
{
Name: "zero-weight cycle^2 confounding directed",
Graph: func() graph.WeightedEdgeAdder { return simple.NewWeightedDirectedGraph(0, math.Inf(1)) },
Edges: []simple.WeightedEdge{
// Add a path from 0->4 of weight 4
{F: simple.Node(0), T: simple.Node(1), W: 1},
{F: simple.Node(1), T: simple.Node(2), W: 1},
{F: simple.Node(2), T: simple.Node(3), W: 1},
{F: simple.Node(3), T: simple.Node(4), W: 1},
// Add a zero-weight cycle.
{F: simple.Node(1), T: simple.Node(5), W: 0},
{F: simple.Node(5), T: simple.Node(1), W: 0},
// With its own zero-weight cycle.
{F: simple.Node(5), T: simple.Node(6), W: 0},
{F: simple.Node(6), T: simple.Node(5), W: 0},
// But leading to the target.
{F: simple.Node(5), T: simple.Node(4), W: 3},
},
Query: simple.Edge{F: simple.Node(0), T: simple.Node(4)},
Weight: 4,
WantPaths: [][]int64{
{0, 1, 2, 3, 4},
{0, 1, 5, 4},
},
HasUniquePath: false,
NoPathFor: simple.Edge{F: simple.Node(4), T: simple.Node(5)},
},
{
Name: "zero-weight cycle^3 directed",
Graph: func() graph.WeightedEdgeAdder { return simple.NewWeightedDirectedGraph(0, math.Inf(1)) },
Edges: []simple.WeightedEdge{
// Add a path from 0->4 of weight 4
{F: simple.Node(0), T: simple.Node(1), W: 1},
{F: simple.Node(1), T: simple.Node(2), W: 1},
{F: simple.Node(2), T: simple.Node(3), W: 1},
{F: simple.Node(3), T: simple.Node(4), W: 1},
// Add a zero-weight cycle.
{F: simple.Node(1), T: simple.Node(5), W: 0},
{F: simple.Node(5), T: simple.Node(1), W: 0},
// With its own zero-weight cycle.
{F: simple.Node(5), T: simple.Node(6), W: 0},
{F: simple.Node(6), T: simple.Node(5), W: 0},
// With its own zero-weight cycle.
{F: simple.Node(6), T: simple.Node(7), W: 0},
{F: simple.Node(7), T: simple.Node(6), W: 0},
},
Query: simple.Edge{F: simple.Node(0), T: simple.Node(4)},
Weight: 4,
WantPaths: [][]int64{
{0, 1, 2, 3, 4},
},
HasUniquePath: false,
NoPathFor: simple.Edge{F: simple.Node(4), T: simple.Node(5)},
},
{
Name: "zero-weight 3·cycle^2 confounding directed",
Graph: func() graph.WeightedEdgeAdder { return simple.NewWeightedDirectedGraph(0, math.Inf(1)) },
Edges: []simple.WeightedEdge{
// Add a path from 0->4 of weight 4
{F: simple.Node(0), T: simple.Node(1), W: 1},
{F: simple.Node(1), T: simple.Node(2), W: 1},
{F: simple.Node(2), T: simple.Node(3), W: 1},
{F: simple.Node(3), T: simple.Node(4), W: 1},
// Add a zero-weight cycle.
{F: simple.Node(1), T: simple.Node(5), W: 0},
{F: simple.Node(5), T: simple.Node(1), W: 0},
// With 3 of its own zero-weight cycles.
{F: simple.Node(5), T: simple.Node(6), W: 0},
{F: simple.Node(6), T: simple.Node(5), W: 0},
{F: simple.Node(5), T: simple.Node(7), W: 0},
{F: simple.Node(7), T: simple.Node(5), W: 0},
// Each leading to the target.
{F: simple.Node(5), T: simple.Node(4), W: 3},
{F: simple.Node(6), T: simple.Node(4), W: 3},
{F: simple.Node(7), T: simple.Node(4), W: 3},
},
Query: simple.Edge{F: simple.Node(0), T: simple.Node(4)},
Weight: 4,
WantPaths: [][]int64{
{0, 1, 2, 3, 4},
{0, 1, 5, 4},
{0, 1, 5, 6, 4},
{0, 1, 5, 7, 4},
},
HasUniquePath: false,
NoPathFor: simple.Edge{F: simple.Node(4), T: simple.Node(5)},
},
{
Name: "zero-weight reversed 3·cycle^2 confounding directed",
Graph: func() graph.WeightedEdgeAdder { return simple.NewWeightedDirectedGraph(0, math.Inf(1)) },
Edges: []simple.WeightedEdge{
// Add a path from 0->4 of weight 4
{F: simple.Node(0), T: simple.Node(1), W: 1},
{F: simple.Node(1), T: simple.Node(2), W: 1},
{F: simple.Node(2), T: simple.Node(3), W: 1},
{F: simple.Node(3), T: simple.Node(4), W: 1},
// Add a zero-weight cycle.
{F: simple.Node(3), T: simple.Node(5), W: 0},
{F: simple.Node(5), T: simple.Node(3), W: 0},
// With 3 of its own zero-weight cycles.
{F: simple.Node(5), T: simple.Node(6), W: 0},
{F: simple.Node(6), T: simple.Node(5), W: 0},
{F: simple.Node(5), T: simple.Node(7), W: 0},
{F: simple.Node(7), T: simple.Node(5), W: 0},
// Each leading from the source.
{F: simple.Node(0), T: simple.Node(5), W: 3},
{F: simple.Node(0), T: simple.Node(6), W: 3},
{F: simple.Node(0), T: simple.Node(7), W: 3},
},
Query: simple.Edge{F: simple.Node(0), T: simple.Node(4)},
Weight: 4,
WantPaths: [][]int64{
{0, 1, 2, 3, 4},
{0, 5, 3, 4},
{0, 6, 5, 3, 4},
{0, 7, 5, 3, 4},
},
HasUniquePath: false,
NoPathFor: simple.Edge{F: simple.Node(4), T: simple.Node(5)},
},
{
Name: "zero-weight |V|·cycle^(n/|V|) directed",
Graph: func() graph.WeightedEdgeAdder { return simple.NewWeightedDirectedGraph(0, math.Inf(1)) },
Edges: func() []simple.WeightedEdge {
e := []simple.WeightedEdge{
// Add a path from 0->4 of weight 4
{F: simple.Node(0), T: simple.Node(1), W: 1},
{F: simple.Node(1), T: simple.Node(2), W: 1},
{F: simple.Node(2), T: simple.Node(3), W: 1},
{F: simple.Node(3), T: simple.Node(4), W: 1},
}
next := len(e) + 1
// Add n zero-weight cycles.
const n = 100
for i := 0; i < n; i++ {
e = append(e,
simple.WeightedEdge{F: simple.Node(next + i), T: simple.Node(i), W: 0},
simple.WeightedEdge{F: simple.Node(i), T: simple.Node(next + i), W: 0},
)
}
return e
}(),
Query: simple.Edge{F: simple.Node(0), T: simple.Node(4)},
Weight: 4,
WantPaths: [][]int64{
{0, 1, 2, 3, 4},
},
HasUniquePath: false,
NoPathFor: simple.Edge{F: simple.Node(4), T: simple.Node(5)},
},
{
Name: "zero-weight n·cycle directed",
Graph: func() graph.WeightedEdgeAdder { return simple.NewWeightedDirectedGraph(0, math.Inf(1)) },
Edges: func() []simple.WeightedEdge {
e := []simple.WeightedEdge{
// Add a path from 0->4 of weight 4
{F: simple.Node(0), T: simple.Node(1), W: 1},
{F: simple.Node(1), T: simple.Node(2), W: 1},
{F: simple.Node(2), T: simple.Node(3), W: 1},
{F: simple.Node(3), T: simple.Node(4), W: 1},
}
next := len(e) + 1
// Add n zero-weight cycles.
const n = 100
for i := 0; i < n; i++ {
e = append(e,
simple.WeightedEdge{F: simple.Node(next + i), T: simple.Node(1), W: 0},
simple.WeightedEdge{F: simple.Node(1), T: simple.Node(next + i), W: 0},
)
}
return e
}(),
Query: simple.Edge{F: simple.Node(0), T: simple.Node(4)},
Weight: 4,
WantPaths: [][]int64{
{0, 1, 2, 3, 4},
},
HasUniquePath: false,
NoPathFor: simple.Edge{F: simple.Node(4), T: simple.Node(5)},
},
{
Name: "zero-weight bi-directional tree with single exit directed",
Graph: func() graph.WeightedEdgeAdder { return simple.NewWeightedDirectedGraph(0, math.Inf(1)) },
Edges: func() []simple.WeightedEdge {
e := []simple.WeightedEdge{
// Add a path from 0->4 of weight 4
{F: simple.Node(0), T: simple.Node(1), W: 1},
{F: simple.Node(1), T: simple.Node(2), W: 1},
{F: simple.Node(2), T: simple.Node(3), W: 1},
{F: simple.Node(3), T: simple.Node(4), W: 1},
}
// Make a bi-directional tree rooted at node 2 with
// a single exit to node 4 and co-equal cost from
// 2 to 4.
const (
depth = 4
branching = 4
)
next := len(e) + 1
src := 2
var i, last int
for l := 0; l < depth; l++ {
for i = 0; i < branching; i++ {
last = next + i
e = append(e, simple.WeightedEdge{F: simple.Node(src), T: simple.Node(last), W: 0})
e = append(e, simple.WeightedEdge{F: simple.Node(last), T: simple.Node(src), W: 0})
}
src = next + 1
next += branching
}
e = append(e, simple.WeightedEdge{F: simple.Node(last), T: simple.Node(4), W: 2})
return e
}(),
Query: simple.Edge{F: simple.Node(0), T: simple.Node(4)},
Weight: 4,
WantPaths: [][]int64{
{0, 1, 2, 3, 4},
{0, 1, 2, 6, 10, 14, 20, 4},
},
HasUniquePath: false,
NoPathFor: simple.Edge{F: simple.Node(4), T: simple.Node(5)},
},
// Negative weighted graphs.
{
Name: "one edge directed negative",
Graph: func() graph.WeightedEdgeAdder { return simple.NewWeightedDirectedGraph(0, math.Inf(1)) },
Edges: []simple.WeightedEdge{
{F: simple.Node(0), T: simple.Node(1), W: -1},
},
HasNegativeWeight: true,
Query: simple.Edge{F: simple.Node(0), T: simple.Node(1)},
Weight: -1,
WantPaths: [][]int64{
{0, 1},
},
HasUniquePath: true,
NoPathFor: simple.Edge{F: simple.Node(2), T: simple.Node(3)},
},
{
Name: "one edge undirected negative",
Graph: func() graph.WeightedEdgeAdder { return simple.NewWeightedUndirectedGraph(0, math.Inf(1)) },
Edges: []simple.WeightedEdge{
{F: simple.Node(0), T: simple.Node(1), W: -1},
},
HasNegativeWeight: true,
HasNegativeCycle: true,
Query: simple.Edge{F: simple.Node(0), T: simple.Node(1)},
HasNegativeCycleInPath: true,
Weight: math.Inf(-1),
WantPaths: [][]int64{
{0, 1}, // One loop around negative cycle and no lead-in path.
},
NoPathFor: simple.Edge{F: simple.Node(2), T: simple.Node(3)},
},
{
Name: "two path directed negative cycle",
Graph: func() graph.WeightedEdgeAdder { return simple.NewWeightedDirectedGraph(0, math.Inf(1)) },
Edges: []simple.WeightedEdge{
{F: simple.Node(0), T: simple.Node(1), W: 1},
{F: simple.Node(1), T: simple.Node(2), W: -1},
{F: simple.Node(2), T: simple.Node(1), W: -1},
{F: simple.Node(1), T: simple.Node(3), W: 1},
{F: simple.Node(0), T: simple.Node(4), W: 1},
},
HasNegativeWeight: true,
HasNegativeCycle: true,
Query: simple.Edge{F: simple.Node(0), T: simple.Node(3)},
HasNegativeCycleInPath: true,
Weight: math.Inf(-1),
WantPaths: [][]int64{
{1, 2, 1, 3}, // One loop around negative cycle and no lead-in path.
},
NoPathFor: simple.Edge{F: simple.Node(5), T: simple.Node(6)},
},
{
Name: "two path directed off-path negative cycle",
Graph: func() graph.WeightedEdgeAdder { return simple.NewWeightedDirectedGraph(0, math.Inf(1)) },
Edges: []simple.WeightedEdge{
{F: simple.Node(0), T: simple.Node(1), W: 1},
{F: simple.Node(1), T: simple.Node(2), W: -1},
{F: simple.Node(2), T: simple.Node(1), W: -1},
{F: simple.Node(1), T: simple.Node(3), W: 1},
{F: simple.Node(0), T: simple.Node(4), W: 10}, // Push search into negative cycle.
},
HasNegativeWeight: true,
HasNegativeCycle: true,
Query: simple.Edge{F: simple.Node(0), T: simple.Node(4)},
HasNegativeCycleInPath: false,
Weight: 10,
WantPaths: [][]int64{
{0, 4},
},
HasUniquePath: true,
NoPathFor: simple.Edge{F: simple.Node(5), T: simple.Node(6)},
},
{
Name: "two path directed diamond negative cycle",
Graph: func() graph.WeightedEdgeAdder { return simple.NewWeightedDirectedGraph(0, math.Inf(1)) },
Edges: []simple.WeightedEdge{
{F: simple.Node(0), T: simple.Node(1), W: 1},
{F: simple.Node(1), T: simple.Node(2), W: -1},
{F: simple.Node(2), T: simple.Node(1), W: -1},
{F: simple.Node(1), T: simple.Node(3), W: 1},
{F: simple.Node(0), T: simple.Node(3), W: 10}, // Push search into negative cycle.
},
HasNegativeWeight: true,
HasNegativeCycle: true,
Query: simple.Edge{F: simple.Node(0), T: simple.Node(3)},
HasNegativeCycleInPath: true,
Weight: math.Inf(-1),
WantPaths: [][]int64{
{1, 2, 1, 3}, // One loop around negative cycle and no lead-in path.
},
NoPathFor: simple.Edge{F: simple.Node(5), T: simple.Node(6)},
},
{
Name: "wp graph negative", // http://en.wikipedia.org/w/index.php?title=Johnson%27s_algorithm&oldid=564595231
Graph: func() graph.WeightedEdgeAdder { return simple.NewWeightedDirectedGraph(0, math.Inf(1)) },
Edges: []simple.WeightedEdge{
{F: simple.Node('w'), T: simple.Node('z'), W: 2},
{F: simple.Node('x'), T: simple.Node('w'), W: 6},
{F: simple.Node('x'), T: simple.Node('y'), W: 3},
{F: simple.Node('y'), T: simple.Node('w'), W: 4},
{F: simple.Node('y'), T: simple.Node('z'), W: 5},
{F: simple.Node('z'), T: simple.Node('x'), W: -7},
{F: simple.Node('z'), T: simple.Node('y'), W: -3},
},
HasNegativeWeight: true,
Query: simple.Edge{F: simple.Node('z'), T: simple.Node('y')},
Weight: -4,
WantPaths: [][]int64{
{'z', 'x', 'y'},
},
HasUniquePath: true,
NoPathFor: simple.Edge{F: simple.Node(2), T: simple.Node(3)},
},
{
Name: "roughgarden negative",
Graph: func() graph.WeightedEdgeAdder { return simple.NewWeightedDirectedGraph(0, math.Inf(1)) },
Edges: []simple.WeightedEdge{
{F: simple.Node('a'), T: simple.Node('b'), W: -2},
{F: simple.Node('b'), T: simple.Node('c'), W: -1},
{F: simple.Node('c'), T: simple.Node('a'), W: 4},
{F: simple.Node('c'), T: simple.Node('x'), W: 2},
{F: simple.Node('c'), T: simple.Node('y'), W: -3},
{F: simple.Node('z'), T: simple.Node('x'), W: 1},
{F: simple.Node('z'), T: simple.Node('y'), W: -4},
},
HasNegativeWeight: true,
Query: simple.Edge{F: simple.Node('a'), T: simple.Node('y')},
Weight: -6,
WantPaths: [][]int64{
{'a', 'b', 'c', 'y'},
},
HasUniquePath: true,
NoPathFor: simple.Edge{F: simple.Node(2), T: simple.Node(3)},
},
}