gonum.org/v1/gonum@v0.14.0/graph/path/yen_ksp.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 "sort" 10 11 "gonum.org/v1/gonum/graph" 12 "gonum.org/v1/gonum/graph/iterator" 13 ) 14 15 // YenKShortestPaths returns the k-shortest loopless paths from s to t in g 16 // with path costs no greater than cost beyond the shortest path. 17 // If k is negative, only path cost will be used to limit the set of returned 18 // paths. YenKShortestPaths will panic if g contains a negative edge weight. 19 func YenKShortestPaths(g graph.Graph, k int, cost float64, s, t graph.Node) [][]graph.Node { 20 // See https://en.wikipedia.org/wiki/Yen's_algorithm and 21 // the paper at https://doi.org/10.1090%2Fqam%2F253822. 22 23 _, isDirected := g.(graph.Directed) 24 yk := yenKSPAdjuster{ 25 Graph: g, 26 isDirected: isDirected, 27 } 28 29 if wg, ok := g.(Weighted); ok { 30 yk.weight = wg.Weight 31 } else { 32 yk.weight = UniformCost(g) 33 } 34 35 shortest, weight := DijkstraFrom(s, yk).To(t.ID()) 36 cost += weight // Set cost to absolute cost limit. 37 switch len(shortest) { 38 case 0: 39 return nil 40 case 1: 41 return [][]graph.Node{shortest} 42 } 43 paths := [][]graph.Node{shortest} 44 45 var pot []yenShortest 46 var root []graph.Node 47 for i := int64(1); k < 0 || i < int64(k); i++ { 48 // The spur node ranges from the first node to the next 49 // to last node in the previous k-shortest path. 50 for n := 0; n < len(paths[i-1])-1; n++ { 51 yk.reset() 52 53 spur := paths[i-1][n] 54 root := append(root[:0], paths[i-1][:n+1]...) 55 56 for _, path := range paths { 57 if len(path) <= n { 58 continue 59 } 60 ok := true 61 for x := 0; x < len(root); x++ { 62 if path[x].ID() != root[x].ID() { 63 ok = false 64 break 65 } 66 } 67 if ok { 68 yk.removeEdge(path[n].ID(), path[n+1].ID()) 69 } 70 } 71 for _, u := range root[:len(root)-1] { 72 yk.removeNode(u.ID()) 73 } 74 75 spath, weight := DijkstraFrom(spur, yk).To(t.ID()) 76 if weight > cost || math.IsInf(weight, 1) { 77 continue 78 } 79 if len(root) > 1 { 80 var rootWeight float64 81 for x := 1; x < len(root); x++ { 82 w, _ := yk.weight(root[x-1].ID(), root[x].ID()) 83 rootWeight += w 84 } 85 spath = append(root[:len(root)-1], spath...) 86 weight += rootWeight 87 } 88 pot = append(pot, yenShortest{spath, weight}) 89 } 90 91 if len(pot) == 0 { 92 break 93 } 94 95 sort.Sort(byPathWeight(pot)) 96 best := pot[0] 97 if len(best.path) <= 1 || best.weight > cost { 98 break 99 } 100 paths = append(paths, best.path) 101 pot = pot[1:] 102 } 103 104 return paths 105 } 106 107 // yenShortest holds a path and its weight for sorting. 108 type yenShortest struct { 109 path []graph.Node 110 weight float64 111 } 112 113 type byPathWeight []yenShortest 114 115 func (s byPathWeight) Len() int { return len(s) } 116 func (s byPathWeight) Swap(i, j int) { s[i], s[j] = s[j], s[i] } 117 func (s byPathWeight) Less(i, j int) bool { return s[i].weight < s[j].weight } 118 119 // yenKSPAdjuster allows walked edges to be omitted from a graph 120 // without altering the embedded graph. 121 type yenKSPAdjuster struct { 122 graph.Graph 123 isDirected bool 124 125 // weight is the edge weight function 126 // used for shortest path calculation. 127 weight Weighting 128 129 // visitedNodes holds the nodes that have 130 // been removed by Yen's algorithm. 131 visitedNodes map[int64]struct{} 132 133 // visitedEdges holds the edges that have 134 // been removed by Yen's algorithm. 135 visitedEdges map[[2]int64]struct{} 136 } 137 138 func (g yenKSPAdjuster) From(id int64) graph.Nodes { 139 if _, blocked := g.visitedNodes[id]; blocked { 140 return graph.Empty 141 } 142 nodes := graph.NodesOf(g.Graph.From(id)) 143 for i := 0; i < len(nodes); { 144 if g.canWalk(id, nodes[i].ID()) { 145 i++ 146 continue 147 } 148 nodes[i] = nodes[len(nodes)-1] 149 nodes = nodes[:len(nodes)-1] 150 } 151 if len(nodes) == 0 { 152 return graph.Empty 153 } 154 return iterator.NewOrderedNodes(nodes) 155 } 156 157 func (g yenKSPAdjuster) canWalk(u, v int64) bool { 158 if _, blocked := g.visitedNodes[v]; blocked { 159 return false 160 } 161 _, blocked := g.visitedEdges[[2]int64{u, v}] 162 return !blocked 163 } 164 165 func (g yenKSPAdjuster) removeNode(u int64) { 166 g.visitedNodes[u] = struct{}{} 167 } 168 169 func (g yenKSPAdjuster) removeEdge(u, v int64) { 170 g.visitedEdges[[2]int64{u, v}] = struct{}{} 171 if !g.isDirected { 172 g.visitedEdges[[2]int64{v, u}] = struct{}{} 173 } 174 } 175 176 func (g *yenKSPAdjuster) reset() { 177 g.visitedNodes = make(map[int64]struct{}) 178 g.visitedEdges = make(map[[2]int64]struct{}) 179 } 180 181 func (g yenKSPAdjuster) Weight(xid, yid int64) (w float64, ok bool) { 182 return g.weight(xid, yid) 183 }