mirror of
				https://github.com/gonum/gonum.git
				synced 2025-10-31 02:26:59 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			655 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			655 lines
		
	
	
		
			20 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 (
 | |
| 	"fmt"
 | |
| 	"math"
 | |
| 
 | |
| 	"gonum.org/v1/gonum/graph"
 | |
| 	"gonum.org/v1/gonum/graph/simple"
 | |
| )
 | |
| 
 | |
| func init() {
 | |
| 	for _, test := range ShortestPathTests {
 | |
| 		if len(test.WantPaths) != 1 && test.HasUniquePath {
 | |
| 			panic(fmt.Sprintf("%q: bad shortest path test: non-unique paths marked unique", test.Name))
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // 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
 | |
| 
 | |
| 	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)},
 | |
| 	},
 | |
| 	{
 | |
| 		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)},
 | |
| 	},
 | |
| }
 | 
