mirror of
https://github.com/gonum/gonum.git
synced 2025-10-06 07:37:03 +08:00
721 lines
22 KiB
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)},
|
|
},
|
|
}
|