github.com/jingcheng-WU/gonum@v0.9.1-0.20210323123734-f1a2a11a8f7b/graph/path/yen_ksp_test.go (about)

     1  // Copyright ©2018 The Gonum Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package path
     6  
     7  import (
     8  	"math"
     9  	"reflect"
    10  	"sort"
    11  	"testing"
    12  
    13  	"github.com/jingcheng-WU/gonum/graph"
    14  	"github.com/jingcheng-WU/gonum/graph/internal/ordered"
    15  	"github.com/jingcheng-WU/gonum/graph/simple"
    16  )
    17  
    18  var yenShortestPathTests = []struct {
    19  	name  string
    20  	graph func() graph.WeightedEdgeAdder
    21  	edges []simple.WeightedEdge
    22  
    23  	query     simple.Edge
    24  	k         int
    25  	wantPaths [][]int64
    26  
    27  	relaxed bool
    28  }{
    29  	{
    30  		// https://en.wikipedia.org/w/index.php?title=Yen%27s_algorithm&oldid=841018784#Example
    31  		name:  "wikipedia example",
    32  		graph: func() graph.WeightedEdgeAdder { return simple.NewWeightedDirectedGraph(0, math.Inf(1)) },
    33  		edges: []simple.WeightedEdge{
    34  			{F: simple.Node('C'), T: simple.Node('D'), W: 3},
    35  			{F: simple.Node('C'), T: simple.Node('E'), W: 2},
    36  			{F: simple.Node('E'), T: simple.Node('D'), W: 1},
    37  			{F: simple.Node('D'), T: simple.Node('F'), W: 4},
    38  			{F: simple.Node('E'), T: simple.Node('F'), W: 2},
    39  			{F: simple.Node('E'), T: simple.Node('G'), W: 3},
    40  			{F: simple.Node('F'), T: simple.Node('G'), W: 2},
    41  			{F: simple.Node('F'), T: simple.Node('H'), W: 1},
    42  			{F: simple.Node('G'), T: simple.Node('H'), W: 2},
    43  		},
    44  		query: simple.Edge{F: simple.Node('C'), T: simple.Node('H')},
    45  		k:     3,
    46  		wantPaths: [][]int64{
    47  			{'C', 'E', 'F', 'H'},
    48  			{'C', 'E', 'G', 'H'},
    49  			{'C', 'D', 'F', 'H'},
    50  		},
    51  	},
    52  	{
    53  		name:  "1 edge graph",
    54  		graph: func() graph.WeightedEdgeAdder { return simple.NewWeightedDirectedGraph(0, math.Inf(1)) },
    55  		edges: []simple.WeightedEdge{
    56  			{F: simple.Node(0), T: simple.Node(1), W: 3},
    57  		},
    58  		query: simple.Edge{F: simple.Node(0), T: simple.Node(1)},
    59  		k:     10,
    60  		wantPaths: [][]int64{
    61  			{0, 1},
    62  		},
    63  	},
    64  	{
    65  		name:      "empty graph",
    66  		graph:     func() graph.WeightedEdgeAdder { return simple.NewWeightedDirectedGraph(0, math.Inf(1)) },
    67  		edges:     []simple.WeightedEdge{},
    68  		query:     simple.Edge{F: simple.Node(0), T: simple.Node(1)},
    69  		k:         1,
    70  		wantPaths: nil,
    71  	},
    72  	{
    73  		name:  "n-star graph",
    74  		graph: func() graph.WeightedEdgeAdder { return simple.NewWeightedDirectedGraph(0, math.Inf(1)) },
    75  		edges: []simple.WeightedEdge{
    76  			{F: simple.Node(0), T: simple.Node(1), W: 3},
    77  			{F: simple.Node(0), T: simple.Node(2), W: 3},
    78  			{F: simple.Node(0), T: simple.Node(3), W: 3},
    79  		},
    80  		query: simple.Edge{F: simple.Node(0), T: simple.Node(1)},
    81  		k:     1,
    82  		wantPaths: [][]int64{
    83  			{0, 1},
    84  		},
    85  	},
    86  	{
    87  		name:  "bipartite small",
    88  		graph: func() graph.WeightedEdgeAdder { return simple.NewWeightedDirectedGraph(0, math.Inf(1)) },
    89  		edges: bipartite(5, 3, 0),
    90  		query: simple.Edge{F: simple.Node(-1), T: simple.Node(1)},
    91  		k:     10,
    92  		wantPaths: [][]int64{
    93  			{-1, 2, 1},
    94  			{-1, 3, 1},
    95  			{-1, 4, 1},
    96  			{-1, 5, 1},
    97  			{-1, 6, 1},
    98  		},
    99  	},
   100  	{
   101  		name:  "bipartite parity",
   102  		graph: func() graph.WeightedEdgeAdder { return simple.NewWeightedDirectedGraph(0, math.Inf(1)) },
   103  		edges: bipartite(5, 3, 0),
   104  		query: simple.Edge{F: simple.Node(-1), T: simple.Node(1)},
   105  		k:     5,
   106  		wantPaths: [][]int64{
   107  			{-1, 2, 1},
   108  			{-1, 3, 1},
   109  			{-1, 4, 1},
   110  			{-1, 5, 1},
   111  			{-1, 6, 1},
   112  		},
   113  	},
   114  	{
   115  		name:    "bipartite large",
   116  		graph:   func() graph.WeightedEdgeAdder { return simple.NewWeightedDirectedGraph(0, math.Inf(1)) },
   117  		edges:   bipartite(10, 3, 0),
   118  		query:   simple.Edge{F: simple.Node(-1), T: simple.Node(1)},
   119  		k:       5,
   120  		relaxed: true,
   121  	},
   122  	{
   123  		name:  "bipartite inc",
   124  		graph: func() graph.WeightedEdgeAdder { return simple.NewWeightedDirectedGraph(0, math.Inf(1)) },
   125  		edges: bipartite(5, 10, 1),
   126  		query: simple.Edge{F: simple.Node(-1), T: simple.Node(1)},
   127  		k:     5,
   128  		wantPaths: [][]int64{
   129  			{-1, 2, 1},
   130  			{-1, 3, 1},
   131  			{-1, 4, 1},
   132  			{-1, 5, 1},
   133  			{-1, 6, 1},
   134  		},
   135  	},
   136  	{
   137  		name:  "bipartite dec",
   138  		graph: func() graph.WeightedEdgeAdder { return simple.NewWeightedDirectedGraph(0, math.Inf(1)) },
   139  		edges: bipartite(5, 10, -1),
   140  		query: simple.Edge{F: simple.Node(-1), T: simple.Node(1)},
   141  		k:     5,
   142  		wantPaths: [][]int64{
   143  			{-1, 6, 1},
   144  			{-1, 5, 1},
   145  			{-1, 4, 1},
   146  			{-1, 3, 1},
   147  			{-1, 2, 1},
   148  		},
   149  	},
   150  }
   151  
   152  func bipartite(n int, weight, inc float64) []simple.WeightedEdge {
   153  	var edges []simple.WeightedEdge
   154  	for i := 2; i < n+2; i++ {
   155  		edges = append(edges,
   156  			simple.WeightedEdge{F: simple.Node(-1), T: simple.Node(i), W: weight},
   157  			simple.WeightedEdge{F: simple.Node(i), T: simple.Node(1), W: weight},
   158  		)
   159  		weight += inc
   160  	}
   161  	return edges
   162  }
   163  
   164  func pathIDs(paths [][]graph.Node) [][]int64 {
   165  	if paths == nil {
   166  		return nil
   167  	}
   168  	ids := make([][]int64, len(paths))
   169  	for i, p := range paths {
   170  		if p == nil {
   171  			continue
   172  		}
   173  		ids[i] = make([]int64, len(p))
   174  		for j, n := range p {
   175  			ids[i][j] = n.ID()
   176  		}
   177  	}
   178  	return ids
   179  }
   180  
   181  func TestYenKSP(t *testing.T) {
   182  	t.Parallel()
   183  	for _, test := range yenShortestPathTests {
   184  		g := test.graph()
   185  		for _, e := range test.edges {
   186  			g.SetWeightedEdge(e)
   187  		}
   188  
   189  		got := YenKShortestPaths(g.(graph.Graph), test.k, test.query.From(), test.query.To())
   190  		gotIDs := pathIDs(got)
   191  
   192  		paths := make(byPathWeight, len(gotIDs))
   193  		for i, p := range got {
   194  			paths[i] = yenShortest{path: p, weight: pathWeight(p, g.(graph.Weighted))}
   195  		}
   196  		if !sort.IsSorted(paths) {
   197  			t.Errorf("unexpected result for %q: got:%+v", test.name, paths)
   198  		}
   199  		if test.relaxed {
   200  			continue
   201  		}
   202  
   203  		if len(gotIDs) != 0 {
   204  			first := 0
   205  			last := pathWeight(got[0], g.(graph.Weighted))
   206  			for i := 1; i < len(got); i++ {
   207  				w := pathWeight(got[i], g.(graph.Weighted))
   208  				if w == last {
   209  					continue
   210  				}
   211  				sort.Sort(ordered.BySliceValues(gotIDs[first:i]))
   212  				first = i
   213  				last = w
   214  			}
   215  			sort.Sort(ordered.BySliceValues(gotIDs[first:]))
   216  		}
   217  
   218  		if !reflect.DeepEqual(test.wantPaths, gotIDs) {
   219  			t.Errorf("unexpected result for %q:\ngot: %v\nwant:%v", test.name, gotIDs, test.wantPaths)
   220  		}
   221  	}
   222  }
   223  
   224  func pathWeight(path []graph.Node, g graph.Weighted) float64 {
   225  	switch len(path) {
   226  	case 0:
   227  		return math.NaN()
   228  	case 1:
   229  		return 0
   230  	default:
   231  		var w float64
   232  		for i, u := range path[:len(path)-1] {
   233  			_w, _ := g.Weight(u.ID(), path[i+1].ID())
   234  			w += _w
   235  		}
   236  		return w
   237  	}
   238  }